前提
Java8中除了lambda表达式,Stream API(java.util.stream.*)也是神一样的存在,尤其在简化代码,提高代码质量上,十分推荐使用!
何为Stream?
简单说,Stream API提供高效而且易于使用的处理数据方式。Stream是Java8中处理集合的关键抽象概念,可以对集合进行非常复杂的查找,过滤和映射等操作。利用Stream API对集合数据进行操作,类似于SQL执行的数据库查询,而且还可以使用Stream API进行并行执行处理。那么Steam API到底是什么?是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。如果说集合讲的的数据,那么流讲的就是计算!
注意:
1、Stream自身不存储元素
2、Steam不改变源对象,会返回一个持有结果的新Stream
3、Stream操作是延迟执行的。会等到需要结果的时候才开始执行
执行步骤
1、创建Stream
一个数据源,获取一个流
2、中间操作
一个中间操作链,对数据源的数据进行处理
3、终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
创建Stream
1、Java8中Collection接口被扩展,提供两种获取流的方法 stream() 返回一个顺序流;parallelStream()返回一个并行流
2、Arrays中静态方法stream()获取数组流
3、通过stream类中的静态方法of()
4、通过stream类中静态方法iterate()和generate()创建无限流
// 1、可以通过Collection系列集合提供的stream()或parallelStream()(并行流)
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
// parallelStream()(并行流)
Stream<String> parallelStream = list.parallelStream();
// 2、通过Arrays中静态方法stream()获取数组流
Employess[] emps = new Employess[10];
Stream<Employess> stream2 = Arrays.stream(emps);
// 3、通过stream类中的静态方法of()
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
// 4、创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2).limit(10);
// 生成
Stream.generate(() -> Math.random()).limit(5);并行流是把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8 中将并行进行优化,通过stream API使用parallel和sequential()在并行流和顺序流中切换。
Fork/Join框架:在必要情况下,将大任务拆分若干个小任务(拆到不可再拆时),再将一个个小任务运算的结果进行join汇总
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能 。
/**
* 使用FrokJoin框架
*/
@Test
public void test1() {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinCalculate task = new ForkJoinCalculate(0, 10000000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
}
/**
* 普通for
*
* 注意两者相比数据量特别大的时候,使用FrokJoin框架更有效,因为Frok/Join 需要对任务拆分会花费一定时间,如果
* 数据量较小的时候可能拆分时间比运行时间花费更多
*/
public void test2() {
Instant start = Instant.now();
long sum = 0L;
for (int i = 0; i < 10000000000L; i++) {
sum +=i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
}
/**
* Java8 并行流
*/
@Test
public void tes3() {
Instant start = Instant.now();
// parallerl Java8并行流
LongStream.rangeClosed(0, 10000000000L)
.parallel()
.reduce(0, Long::sum);
Instant end = Instant.now();
System.out.println("耗费时间:"+Duration.between(start, end).toMillis());
}中间操作
多个中间操作可以连接形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而且在终止操作时一次性全部处理完,称为“惰性求值”
//代码块所用到实体和集合
@Data
public class Employess {
private Integer code;
private String name;
private int age;
private Double money;
private Status status;
public Employess(Integer code, String name, int age, Double money) {
this.code = code;
this.name = name;
this.age = age;
this.money = money;
}
public Employess(Integer code, String name, int age, Double money, Status status) {
this.code = code;
this.name = name;
this.age = age;
this.money = money;
this.status = status;
}
@Override
public String toString() {
return "Employess{" +
"code=" + code +
", name='" + name + '\'' +
", age=" + age +
", money=" + money +
", status=" + status +
'}';
}
public Employess() {
}
public Employess(Integer code) {
this.code = code;
}
public enum Status {
FREE,
BUSY,
VOCATION;
}
}
List<Employess> emps = Arrays.asList(
new Employess(101, "张三", 18, 9999.99),
new Employess(102, "李四", 59, 6666.66),
new Employess(103, "王五", 28, 3333.33),
new Employess(104, "赵六", 8, 7777.77),
new Employess(105, "田七", 38, 5555.55),
new Employess(105, "田七", 38, 5555.55)
);1、筛选与切片
filter(Predicate p):接收lambda,从流中排除某些元素
limit(long maxSize):截断流,使其得到元素不会超过给定数量
skip(long n):跳过元素,返回之前扔掉前n个元素,如不足,则返回空流,与limit互补
distinct():筛选,根据流所生成元素的hashCode()和equals()去除重复元素
//中间操作-filter
// 如果没有终止操作,中间操作本质上也是不执行的
System.out.println("----------filter--------------");
Stream<Employess> stream = emps.stream()
//筛选年龄大于35,而且工资大于6000
.filter((e) -> e.getAge() > 35)
.filter((e -> e.getMoney() > 6000));
//终止操作
stream.forEach(System.out::println);
System.out.println("----------limit--------------");
//只需前两个元素
emps.stream().limit(2).forEach(System.out::println);
System.out.println("----------skip--------------");
//去除前两个元素
emps.stream().skip(2).forEach(System.out::println);
System.out.println("----------distinct--------------");
//去除重复元素
emps.stream().distinct().forEach(System.out::println);
2、映射
map(Function f):接收lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会应用到每个元素上,并将其映射成一个新的元素
flatMap(Function f):接收lambda函数做参数,将流中每个值都转换为一个另一个流,然后把所有流连接成一个流
mapToDouble(ToDoubleFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的DoubleStream。
mapToLong(ToLongFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的LongStream。
mapToInt(ToIntFunction f):接收一个函数作为参数,将该函数应用到每个元素上,产生一个新的IntStream。
(flatMap也有上述三种类型的stream)
// map从集合中提取元素,把应用函数之后的元素取出放到新的stream中
System.out.println("-----------map---------------");
emps.stream()
.map(Employess::getName)
.forEach(System.out::println);
System.out.println("-----------map函数 VS flatMap函数---------------");
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
System.out.println("-----------map函数---------------");
// map用于从集合中提取元素,将整个集合都放到Map中,类似list.add(list);
Stream<Stream<Character>> mapStream = list.stream().map(TestStreamAPI2::filterCharacter);
//返回值为 Stream<Stream<Character>> mapStream 类似于{{a,a,a},{b,b,b},{c,c,d}}
mapStream.forEach((s)-> {
System.out.print("{");
s.forEach(System.out::print);
System.out.print("}");
});
System.out.println();
System.out.println("-----------flatMap函数---------------");
// 将流中每个元素都整合flatMap这个流中 list.addAll(list)
Stream<Character> flatMapStream = list.stream().flatMap(TestStreamAPI2::filterCharacter);
//返回值为 Stream<Character> {a,a,a,b,b,b,c,c,c}
flatMapStream.forEach(System.out::print);
3、排序
sorted():产生一个新流,按自然顺序排序
sorted(Comparator comp):产生一个新流,其中按比较器顺序排序
System.out.println("------------soreted------------------");
emps.stream().map(Employess::getName).distinct().sorted().forEach(System.out::println);
System.out.println("------------sorted(Comparator com)------------------");
emps.stream().distinct().sorted((x, y) -> {
if (x.getAge() == y.getAge()) {
return x.getName().compareTo(y.getName());
} else {
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
终止操作
终止操作会从流的流水线生成结果。结果可以是任何不是流的值。
查找与匹配
* allMatch-检查是否匹配所有元素
* anyMatch-检查是否至少匹配一个元素
* noneMatch-检查是否没有匹配所有元素
* findFirst-返回第一个元素
* findAny-返回当前流中的任意元素
* count-返回流中元素的总个数
* max-返回流中最大值
* min-返回流中最小值
boolean b1 = emps.stream()
.allMatch(e -> e.getCode().equals("101"));
System.out.println(b1);归约
reduce(T identity,BinaryOperator)/reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值
System.out.println("-------------reduce(T identity,BinaryOperator)--------------");
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("--------------map-reduce--------------");
// map 和reduce的连接通常为map-reduce模式
Double slary = emps.stream()
.map(Employess::getMoney)
.reduce(0.0, (x, y) -> x + y);
System.out.println(slary);
System.out.println("-----------reduce(BinaryOperator)-----------------");
Optional<Double> op = emps.stream()
.map(Employess::getMoney)
.reduce(Double::sum);
System.out.println(op.get());
收集
collect——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总方法,这个特别强大,下面小编列举简单的使用方式。
存放到指定的集合中
//放到list中
System.out.println("--------list-----------------");
List<String> list = emps.stream()
.map(Employess::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
//可以将数据存放到指定集合中
System.out.println("--------自定义集合-----------------");
HashSet<String> hashSet = emps.stream()
.map(Employess::getName)
.collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);可以通过这种方式获取平均值,总数,个数,最大值,最小值之类内容
//获取总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
// 求平均数
Double ave = emps.stream()
.collect(Collectors.averagingDouble(Employess::getMoney));
System.out.println(ave);分组
//普通分组
System.out.println("-------------普通分组-------------");
Map<Employess.Status, List<Employess>> map1 = emps.stream()
.collect(Collectors.groupingBy(Employess::getStatus));
System.out.println(map1);
//多级分组
System.out.println("-------------多级分组-------------");
Map<Employess.Status, Map<String, List<Employess>>> map = emps.stream().collect(Collectors.groupingBy(Employess::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() >= 60) {
return "老年";
} else if (e.getAge() >= 35) {
return "中年";
} else {
return "青年";
}
})));
System.out.println(map);分区
Map<Boolean, List<Employess>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getMoney() > 8000));
System.out.println(map);拼接
String str = emps.stream()
.map(Employess::getName)
.collect(Collectors.joining(",","==","==="));
总结
Stream API强大的功能还有很多,具体的还要详细学习API文档,看了Stream API的使用教程,小编脑海只有一个想法,还是默默地去重构自己的代码,才理解为什么别人那么嫌弃自己的代码,汗颜啊!

本文详细介绍Java8中的StreamAPI,包括Stream的概念、创建方法、并行流的应用、中间操作和终止操作等内容,并通过实例演示如何使用StreamAPI简化代码。
1634

被折叠的 条评论
为什么被折叠?



