Stream流是什么?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
常用方法举例:
1.1筛选与切片:
中间操作:
- filter:接收 Lambda ,从流中排除某些元素
- limit:截断流,使其元素不超过给定数量
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
List<Employee>emps=Arrays.asList( newEmployee(101, "Z3", 19, 9999.99), newEmployee(102, "L4", 20, 7777.77), newEmployee(103, "W5", 35, 6666.66), newEmployee(104, "Tom", 44, 1111.11), newEmployee(105, "Jerry", 60, 4444.44) ); publicvoidtest01(){ emps.stream() .filter((x) ->x.getAge() >35) .limit(3) //短路?达到满足不再内部迭代 .distinct() .skip(1) .forEach(System.out::println); }
- 内部迭代:迭代操作由 Stream API 完成
- 外部迭代:我们通过迭代器完成
1.2映射:
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
map和flatmap相当于add 和addAll的区别:
add:对("a", "b", "c")以一个集合的形式注入到新数组中 ( "1" ," 2 ", ("a", "b", "c"))
addAll: 把("a", "b", "c")以每个元素直接注入数组中( "1" ," 2 ","a", "b", "c")
map:
publicvoidtest02(){ List<String>list=Arrays.asList("a", "b", "c"); list.stream() .map((str) ->str.toUpperCase())//变为A,B,C .forEach(System.out::println); }
flatMap:
publicStream<Character>filterCharacter(Stringstr){ List<Character>list=newArrayList<>(); for (charc : str.toCharArray()) { list.add(c); } returnlist.stream(); } publicvoidtest03(){ List<String>list=Arrays.asList("a", "b", "c"); Test02test02=newTest02(); list.stream() .flatMap(test02::filterCharacter) .forEach(System.out::println); }
1.3排序:
- sorted():自然排序
- sorted(Comparator c):定制排序
Comparable:自然排序
publicvoidtest04(){ List<Integer>list=Arrays.asList(1,2,3,4,5); list.stream() .sorted() //comparaTo() .forEach(System.out::println); }
Comparator:定制排序
publicvoidtest05(){ emps.stream() .sorted((e1, e2) -> { //compara()if (e1.getAge().equals(e2.getAge())){ returne1.getName().compareTo(e2.getName()); } else { returne1.getAge().compareTo(e2.getAge()); } }) .forEach(System.out::println); }
1.4查找/匹配:
终止操作:
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
publicenumStatus { FREE, BUSY, VOCATION; } publicvoidtest01(){ List<Status>list=Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION); booleanflag1=list.stream() .allMatch((s) ->s.equals(Status.BUSY)); System.out.println(flag1); booleanflag2=list.stream() .anyMatch((s) ->s.equals(Status.BUSY)); System.out.println(flag2); booleanflag3=list.stream() .noneMatch((s) ->s.equals(Status.BUSY)); System.out.println(flag3); // 避免空指针异常Optional<Status>op1=list.stream() .findFirst(); // 如果Optional为空 找一个替代的对象Statuss1=op1.orElse(Status.BUSY); System.out.println(s1); Optional<Status>op2=list.stream() .findAny(); System.out.println(op2); longcount=list.stream() .count(); System.out.println(count); }
1.5 归约 / 收集:
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
/*** Java:* - reduce:需提供默认值(初始值)* Kotlin:* - fold:不需要默认值(初始值)* - reduce:需提供默认值(初始值)*/publicvoidtest01(){ List<Integer>list=Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); Integerinteger=list.stream() .reduce(0, (x, y) ->x+y); System.out.println(integer); }
collect:
List<Employee>emps=Arrays.asList( newEmployee(101, "Z3", 19, 9999.99), newEmployee(102, "L4", 20, 7777.77), newEmployee(103, "W5", 35, 6666.66), newEmployee(104, "Tom", 44, 1111.11), newEmployee(105, "Jerry", 60, 4444.44) ); publicvoidtest02(){ //放入ListList<String>list=emps.stream() .map(Employee::getName) .collect(Collectors.toList()); list.forEach(System.out::println); //放入SetSet<String>set=emps.stream() .map(Employee::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); //放入LinkedHashSetLinkedHashSet<String>linkedHashSet=emps.stream() .map(Employee::getName) .collect(Collectors.toCollection(LinkedHashSet::new)); linkedHashSet.forEach(System.out::println); } publicvoidtest03(){ //总数Longcount=emps.stream() .collect(Collectors.counting()); System.out.println(count); //平均值Doubleavg=emps.stream() .collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); //总和Doublesum=emps.stream() .collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); //最大值Optional<Employee>max=emps.stream() .collect(Collectors.maxBy((e1, e2) ->Double.compare(e1.getSalary(), e2.getSalary()))); System.out.println(max.get()); //最小值Optional<Double>min=emps.stream() .map(Employee::getSalary) .collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); } publicvoidtest04(){ //分组Map<Integer, List<Employee>>map=emps.stream() .collect(Collectors.groupingBy(Employee::getId)); System.out.println(map); //多级分组Map<Integer, Map<String, List<Employee>>>mapMap=emps.stream() .collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> { if (e.getAge() >35) { return"开除"; } else { return"继续加班"; } }))); System.out.println(mapMap); //分区Map<Boolean, List<Employee>>listMap=emps.stream() .collect(Collectors.partitioningBy((e) ->e.getSalary() >4321)); System.out.println(listMap); } publicvoidtest05(){ //总结DoubleSummaryStatisticsdss=emps.stream() .collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(dss.getMax()); System.out.println(dss.getMin()); System.out.println(dss.getSum()); System.out.println(dss.getCount()); System.out.println(dss.getAverage()); //连接Stringstr=emps.stream() .map(Employee::getName) .collect(Collectors.joining("-")); //可传入分隔符System.out.println(str); }
1.6 案例:
**案例一:**给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【1,2,3, 4,5】,返回【1,4,9,16,25】)
publicvoidtest01(){ List<Integer>list=Arrays.asList(1, 2, 3, 4, 5); list.stream() .map((x) ->x*x) .forEach(System.out::println); }
**案例二:**怎样使用 map 和 reduce 数一数流中有多少个 Employee 呢?
List<Employee>emps=Arrays.asList( newEmployee(101, "Z3", 19, 9999.99), newEmployee(102, "L4", 20, 7777.77), newEmployee(103, "W5", 35, 6666.66), newEmployee(104, "Tom", 44, 1111.11), newEmployee(105, "Jerry", 60, 4444.44) ); publicvoidtest02(){ Optional<Integer>result=emps.stream() .map((e) ->1) .reduce(Integer::sum); System.out.println(result.get());
1.7:并行流:
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
Java 8 并行流 / 串行流:
publicvoidtest03(){ //串行流(单线程):切换为并行流 parallel()//并行流:切换为串行流 sequential()LongStream.rangeClosed(0, 100000000L) .parallel() //底层:ForkJoin .reduce(0, Long::sum); }