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( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) ); @Test public void test01(){ 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:
@Test public void test02(){ List<String> list = Arrays.asList("a", "b", "c"); list.stream() .map((str) -> str.toUpperCase())//变为A,B,C .forEach(System.out::println); }
flatMap:
public Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (char c : str.toCharArray()) { list.add(c); } return list.stream(); } @Test public void test03(){ List<String> list = Arrays.asList("a", "b", "c"); Test02 test02 = new Test02(); list.stream() .flatMap(test02::filterCharacter) .forEach(System.out::println); }
1.3排序:
-
- sorted():自然排序
- sorted(Comparator c):定制排序
Comparable:自然排序
@Test public void test04(){ List<Integer> list = Arrays.asList(1,2,3,4,5); list.stream() .sorted() //comparaTo() .forEach(System.out::println); }
Comparator:定制排序
@Test public void test05(){ emps.stream() .sorted((e1, e2) -> { //compara() if (e1.getAge().equals(e2.getAge())){ return e1.getName().compareTo(e2.getName()); } else { return e1.getAge().compareTo(e2.getAge()); } }) .forEach(System.out::println); }
1.4查找/匹配:
终止操作:
-
-
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
-
public enum Status { FREE, BUSY, VOCATION; } @Test public void test01(){ List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION); boolean flag1 = list.stream() .allMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag1); boolean flag2 = list.stream() .anyMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag2); boolean flag3 = list.stream() .noneMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag3); // 避免空指针异常 Optional<Status> op1 = list.stream() .findFirst(); // 如果Optional为空 找一个替代的对象 Status s1 = op1.orElse(Status.BUSY); System.out.println(s1); Optional<Status> op2 = list.stream() .findAny(); System.out.println(op2); long count = list.stream() .count(); System.out.println(count); }
1.5 归约 / 收集:
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
/** * Java: * - reduce:需提供默认值(初始值) * Kotlin: * - fold:不需要默认值(初始值) * - reduce:需提供默认值(初始值) */ @Test public void test01(){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); Integer integer = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(integer); }
collect:
List<Employee> emps = Arrays.asList( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) ); @Test public void test02(){ //放入List List<String> list = emps.stream() .map(Employee::getName) .collect(Collectors.toList()); list.forEach(System.out::println); //放入Set Set<String> set = emps.stream() .map(Employee::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); //放入LinkedHashSet LinkedHashSet<String> linkedHashSet = emps.stream() .map(Employee::getName) .collect(Collectors.toCollection(LinkedHashSet::new)); linkedHashSet.forEach(System.out::println); } @Test public void test03(){ //总数 Long count = emps.stream() .collect(Collectors.counting()); System.out.println(count); //平均值 Double avg = emps.stream() .collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); //总和 Double sum = 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()); } @Test public void test04(){ //分组 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); } @Test public void test05(){ //总结 DoubleSummaryStatistics dss = 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()); //连接 String str = emps.stream() .map(Employee::getName) .collect(Collectors.joining("-")); //可传入分隔符 System.out.println(str); }
1.6 案例:
**案例一:**给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【1,2,3, 4,5】,返回【1,4,9,16,25】)
@Test public void test01(){ 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( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) ); @Test public void test02(){ Optional<Integer> result = emps.stream() .map((e) -> 1) .reduce(Integer::sum); System.out.println(result.get());
1.7:并行流:
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
Fork / Join 实现:
public class ForkJoinCalculate extends RecursiveTask<Long> { private static final long serialVersionUID = 1234567890L; private long start; private long end; private static final long THRESHPLD = 10000; public ForkJoinCalculate(long start, long end) { this.start = start; this.end = end; } @Override protected Long compute() { long length = end - start; if (length <= THRESHPLD) { long sum = 0; for (long i = start; i <= end; i++) { sum += i; } } else { long middle = (start + end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start, end); left.fork(); //拆分子任务 压入线程队列 ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end); right.fork(); return left.join() + right.join(); } return null; } } public class TestForkJoin { /** * ForkJoin 框架 */ @Test public void test01(){ Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool(); ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L); Long sum = pool.invoke(task); System.out.println(sum); Instant end = Instant.now(); System.out.println(Duration.between(start, end).getNano()); } /** * 普通 for循环 */ @Test public void test02(){ Instant start = Instant.now(); Long sum = 0L; for (long i = 0; i < 100000000L; i++) { sum += i; } Instant end = Instant.now(); System.out.println(Duration.between(start, end).getNano()); } }
Java 8 并行流 / 串行流:
@Test public void test03(){ //串行流(单线程):切换为并行流 parallel() //并行流:切换为串行流 sequential() LongStream.rangeClosed(0, 100000000L) .parallel() //底层:ForkJoin .reduce(0, Long::sum); }