文章目录

  • 1. 什么是stream?
  • 2. 如何创建stream?
    • 2.1 空的流
    • 2.2 集合的流
    • 2.3 数组的流
    • 2.4 Stream.builder()
    • 2.5 Stream.generate()
    • 2.6 Stream.iterate()
    • 2.7 字符串的流
    • 2.8 文件的流
    • 2.9 基本类型的流
    • 并行流和顺序流的区别
  • 3. 怎么玩转stream?
    • 3.1 遍历/匹配(foreach/find/match)
    • 3.2 筛选(filter)
    • 3.3 聚合(max/min/count)
      • 3.3.1 获取字符串列表中最长的元素
      • 3.3.2 获取Integer集合中最大的数
      • 3.3.3 获取Double列表中最小的数
      • 3.3.4 获取价格高于35w的车数和最贵的车
    • 3.4 映射(map/flatMap)
      • 3.4.1 字符串集合元素全都转换成大写
      • 3.4.2 数字集合元素全部 ×2 + 5 操作
      • 3.4.3 所有汽车都降价2w
      • 3.4.4 将两个字符数组合并成一个新的字符数组
    • 3.5 归约(reduce)
      • 3.5.1 求Integer集合的元素之和、乘积、最大值
      • 3.5.2 求所有汽车的售价之和,最高价格
    • 3.6 收集(collect)
      • 3.6.1 归集(toList/toSet/toMap)
      • 3.6.2 统计(count/averaging)
      • 3.6.3 分组(partitioningBy/groupingBy)
      • 3.6.4 接合(joining)
      • 3.6.5 归约(reducing)
    • 3.7 排序(sorted)
    • 3.8 提取/组合 【终端操作】
  • 4. 总结

1. 什么是stream?

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

Stream(流)是一个来自数据源的元素队列并支持聚合操作:

基本概念:

  • 元素:特定类型的对象,形成一个队列。
  • 数据源: 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作: 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

流的操作

  • 中间操作: 每次返回一个新的流,可以有多个,如 foreach / filter 等。
  • 终端操作:每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值,如 max / count 等。

特性:

  • stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  • stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  • stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

2. 如何创建stream?

三种方式:

  1. 通过 java.util.Collection.stream() 方法用集合创建流。
  2. 通过 java.util.Arrays.stream(T[] array)方法用数组创建流。
  3. 使用Stream的静态方法:of()、iterate()、generate()。

具体创建方式如下:

2.1 空的流

// 代码
Stream<String> emptyStream = Stream.empty();
System.out.println(emptyStream);// 输出
java.util.stream.ReferencePipeline$Head@4554617c

2.2 集合的流

可以创建任何类型的集合(Collection, List, Set)的流:

// 代码
private static void streamByCollection() {List<String> list = Arrays.asList("l", "f", "c");// 创建顺序流Stream<String> stream = list.stream();// 创建并行流Stream<String> parallelStream = list.parallelStream();System.out.println(stream);System.out.println(parallelStream);}// 输出
java.util.stream.ReferencePipeline$Head@43556938
java.util.stream.ReferencePipeline$Head@3d04a311

2.3 数组的流

// 代码
private static void streamByArrays() {long[] arr = {1, 3, 5, 2, 4, 6};LongStream stream = Arrays.stream(arr);System.out.println(stream);}// 输出
java.util.stream.LongPipeline$Head@3abfe836

2.4 Stream.builder()

使用builder时,应在语句的右侧另外使用的类型,否则build()方法将创建 Stream 的实例:


Stream<String> streamBuilder = Stream.<String>builder().add("L").add("f").add("c").build();

2.5 Stream.generate()

generate()方法接受Supplier 进行元素生成。由于结果流是无限的,因此开发人员应指定所需的大小,否则generate()方法运行后会达到内存的上限:

// 代码
Stream<String> streamGenerated = Stream.generate(() -> "Linfanchen").limit(10);
System.out.println(streamGenerated);// 输出
java.util.stream.SliceOps$1@6adca536

2.6 Stream.iterate()

// 代码
Stream<Integer> streamIterated = Stream.iterate(66, n -> n + 2).limit(4);
System.out.println(streamIterated);
streamIterated.forEach(System.out::println);// 输出
java.util.stream.SliceOps$1@3c5a99da
66
68
70
72

2.7 字符串的流

由于JDK中没有接口CharStream,因此使用IntStream表示字符流。用到了String类的chars()方法。

// 代码
IntStream streamOfChars = "Lfc".chars();
System.out.println(streamOfChars);// 输出
java.util.stream.IntPipeline$Head@6833ce2c

2.8 文件的流

Java NIO类 Files 允许通过lines()方法生成文本文件的Stream 。文本的每一行都成为流的元素:

// 代码
Path path = Paths.get("D:\\study\\javaBase\\oop\\src\\com\\lfc\\stream\\log.txt");try {Stream<String> streamOfStrings = Files.lines(path);Stream<String> streamWithCharset = Files.lines(path, Charset.forName("UTF-8"));streamOfStrings.forEach(System.out::println);streamWithCharset.forEach(System.out::println);} catch (IOException ioe) {System.out.println(ioe.getMessage());}// 输出
abc
abc

2.9 基本类型的流

Java 8提供了从三种基本类型中创建流的方式:int,long,double。
由于Stream 是泛型接口,无法将基本类型用作泛型的类型参数,因此创建了三个新的特殊接口:IntStream,LongStream和DoubleStream。使用新接口可以减轻不必要的自动装箱,从而提效率:

// 代码
IntStream intStream = IntStream.range(1, 5);
LongStream longStream = LongStream.rangeClosed(1, 5);
DoubleStream doubleStream = DoubleStream.of(123.45);System.out.println(intStream);
System.out.println(longStream);
System.out.println(doubleStream);// 输出
java.util.stream.IntPipeline$Head@2ff5659e
java.util.stream.LongPipeline$Head@77afea7d
java.util.stream.DoublePipeline$Head@161cd475

并行流和顺序流的区别

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选一组数中的奇数:

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

3. 怎么玩转stream?

操作:
流程:创建Stream流 --> 中间操作 --> 终端操作

  • 无状态:指元素的处理不受之前元素的影响。
  • 有状态:指该操作只有拿到所有元素之后才能继续下去。
  • 非短路操作:指必须处理所有元素才能得到最终结果。
  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

案例领域模型(应该每个男人都喜欢车吧):

实体类:

/*** 汽车实体类*/
public class Car {// 汽车名称private String name;// 品牌private String brand;// 设计国家private String country;// 颜色private String color;// 寿命private int life;// 价格private int price;public Car(String name, String brand, String country, String color, int life, int price) {this.name = name;this.brand = brand;this.country = country;this.color = color;this.life = life;this.price = price;}// 此处省略getter和setter...
}

业务数据:

private static List<Car> getCars() {List<Car> carList = new ArrayList<>();carList.add(new Car("325Li M运动曜夜", "宝马", "德国", "矿石白", 15, 370000));carList.add(new Car("530Li 尊享型 M运动套装", "宝马", "德国", "碳黑", 16, 520000));carList.add(new Car("740Li xDrive 行政型 豪华套装", "宝马", "德国", "波尔蒂芒蓝", 20, 1100000));carList.add(new Car("C260L 运动版4MATIC", "奔驰", "德国", "贝母白", 10, 410000));carList.add(new Car("E300L 尊贵型", "奔驰", "德国", "曜岩黑", 14, 550000));carList.add(new Car("S500L 4MATIC", "奔驰", "德国", "海岳蓝", 18, 1250000));carList.add(new Car("ES260 卓越版", "雷克萨斯", "日本", "超音速钛银", 20, 375000));carList.add(new Car("ES300h 尊享版", "雷克萨斯", "日本", "凌波玉色", 25, 450000));carList.add(new Car("ES300h 行政版", "雷克萨斯", "日本", "超音速石英白", 30, 552000));carList.add(new Car("CT5 28T铂金型", "凯迪拉克", "美国", "云海白", 6, 306000));carList.add(new Car("CT6 28T尊贵型", "凯迪拉克", "美国", "玛雅黑", 8, 393000));carList.add(new Car("XT6 六座四驱豪华型", "凯迪拉克", "美国", "黛蓝", 11, 435000));return carList;}

3.1 遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。

        List<Integer> list = Arrays.asList(1, 2, 5, 6, 8, 9);// 找出偶数list.stream().filter(n -> n % 2 == 0).forEach(System.out::println);// 找出第一个大于5的偶数Optional<Integer> findFirst = list.stream().filter(n -> (n % 2 == 0 && n > 5)).findFirst();// 找出偶数(通过并行流)Optional<Integer> findAny = list.parallelStream().filter(n -> n > 2).findAny();// 是否包含满足条件的元素boolean anyMatch = list.stream().anyMatch(n -> n > 8);System.out.println("\n 第一个大于5的偶数:" + findFirst.get());System.out.println("\n 通过并行流找出偶数:" + findAny.toString());System.out.println("\n 是否存在大于8的数:" + anyMatch);

3.2 筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

// 代码
List<Car> carList = getCars();List<String> filterList = carList.parallelStream().filter(x -> x.getPrice() > 500000).map(Car::getName).collect(Collectors.toList());System.out.println("价格高于50w的车:" + filterList);// 输出
[530Li 尊享型 M运动套装, 740Li xDrive 行政型 豪华套装, E300L 尊贵型, S500L 4MATIC, ES300h 行政版]

3.3 聚合(max/min/count)

类似于MySQL中的聚合计算。

3.3.1 获取字符串列表中最长的元素

// 代码
List<String> list = Arrays.asList("Java", "php", "c", "c++", "python", "shell", "Go");Optional<String> maxString = list.parallelStream().max(Comparator.comparing(String::length));
System.out.println("字面最长的开发语言是:" + maxString.get());// 输出
字面最长的开发语言是:python

3.3.2 获取Integer集合中最大的数

// 代码
List<Integer> list = Arrays.asList(1, 443, 67, 23, 98, 77);
// 自然排序
Optional<Integer> maxNatural = list.parallelStream().max(Integer::compareTo);// 自定义排序:重写compare()方法
Optional<Integer> maxSelf = list.stream().max(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1.compareTo(o2);}});
System.out.println("自然排序的最大值是:" + maxNatural.get());
System.out.println("自定义排序的最大值是:" + maxSelf.get());// 输出
自然排序的最大值是:443
自定义排序的最大值是:443

3.3.3 获取Double列表中最小的数

// 代码
List<Double> list = Arrays.asList(2.2, 443.2, 67.5, 23.1, 98.9, 77.8);Optional<Double> min = list.parallelStream().min(Double::compareTo);
System.out.println("数组中最小的数值为:" + min.get());// 输出
数组中最小的数值为:2.2

3.3.4 获取价格高于35w的车数和最贵的车

List<Car> carList = getCars();
// 获取价格高于35w的车数量
long count = carList.stream().filter(car -> car.getPrice() > 350000).count();// 获取车价最高的车
Optional<Car> maxCar = carList.stream().max(Comparator.comparing(Car::getPrice));System.out.println("价格高于35w的车数量:" + count);
System.out.println("价格最高的车是:" + maxCar.get().getName());// 输出
价格高于35w的车数量:11
价格最高的车是:S500L 4MATIC

3.4 映射(map/flatMap)

可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。


3.4.1 字符串集合元素全都转换成大写

// 代码
String[] strArr = {"Java", "c", "c++", "php", "Python", "shell", "Go"};
List<String> stringList = Arrays.asList(strArr).stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println("转换成大写之后:" + stringList);// 输出
转换成大写之后:[JAVA, C, C++, PHP, PYTHON, SHELL, GO]

3.4.2 数字集合元素全部 ×2 + 5 操作

// 代码
List<Integer> list = Arrays.asList(1, 3, 5, 7, 6, 8, 9);
List<Integer> listNew = list.stream().map(n -> n * 2 + 5).collect(Collectors.toList());
System.out.println("对整数列表元素操作后的结果:" + listNew);// 输出
对整数列表元素操作后的结果:[7, 11, 15, 19, 17, 21, 23]

3.4.3 所有汽车都降价2w

List<Car> carList = getCars();// 不改变原汽车集合List<Car> carListNew = carList.stream().map(car -> {Car carNew = new Car(car.getName(), car.getBrand(), car.getCountry(), car.getColor(), car.getLife(), 0);carNew.setPrice(car.getPrice() - 20000);return carNew;}).collect(Collectors.toList());System.out.println("不改变原汽车集合降价:" + carListNew);// 改变原汽车集合List<Car> carListNew2 = carList.stream().map(car -> {car.setPrice(car.getPrice() - 20000);return car;}).collect(Collectors.toList());System.out.println("改变原汽车集合降价:" + carListNew2);

3.4.4 将两个字符数组合并成一个新的字符数组

// 代码
List<String> list = Arrays.asList("J-a-v-a", "1-2-6-7-9");
List<String> listNew = list.stream().flatMap(s -> {String[] splitArr = s.split("-");Stream<String> s2 = Arrays.stream(splitArr);return s2;
}).collect(Collectors.toList());System.out.println("处理前:" + list);System.out.println("处理hou:" + listNew);// 输出
处理前:[J-a-v-a, 1-2-6-7-9]
处理hou:[J, a, v, a, 1, 2, 6, 7, 9]

3.5 归约(reduce)

把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

3.5.1 求Integer集合的元素之和、乘积、最大值

List<Integer> list = Arrays.asList(1, 2, 6, 7, 9, 34, 56, 88);// 求和-方式1Optional<Integer> sum1 = list.stream().reduce((x, y) -> x + y);// 求和-方式2Optional<Integer> sum2 = list.stream().reduce(Integer::sum);// 求和-方式3Integer sum3 = list.stream().reduce(0, Integer::sum);// 求积Optional<Integer> product = list.stream().reduce((x, y) -> x * y);// 求最大值-方式1Optional<Integer> max1 = list.stream().reduce((x, y) -> x > y ? x : y);// 求最大值-方式2Integer max2 = list.stream().reduce(1, Integer::max);System.out.println("求和-方式1:" + sum1.get());System.out.println("求和-方式2:" + sum2.get());System.out.println("求和-方式3:" + sum3);System.out.println("求积:" + product.get());System.out.println("求最大值-方式1:" + max1.get());System.out.println("求最大值-方式2:" + max2);

3.5.2 求所有汽车的售价之和,最高价格

List<Car> carList = getCars();// 总价格之和:方式1Optional<Integer> sumPrice = carList.stream().map(Car::getPrice).reduce(Integer::sum);// 总价格之和:方式2Integer sumPrice2 = carList.stream().reduce(0, (sum, car) -> sum += car.getPrice(),(sum1, sum2) -> sum1 + sum2);// 总价格之和:方式3Integer sumPrice3 = carList.stream().reduce(0, (sum, car) -> sum += car.getPrice(), Integer::sum);// 最高售价:方式1Integer maxPrice1 = carList.stream().reduce(0, (max, car) -> max > car.getPrice() ? max : car.getPrice(),Integer::max);// 最高售价:方式2Integer maxPrice2 = carList.stream().reduce(0, (max, car) -> max > car.getPrice() ? max : car.getPrice(),(max1, max2) -> max1 > max2 ? max1 : max2);System.out.println("总价格之和-方式1:" + sumPrice);System.out.println("总价格之和-方式2:" + sumPrice2);System.out.println("总价格之和-方式3:" + sumPrice3);System.out.println("最高售价-方式1:" + maxPrice1);System.out.println("最高售价-方式2:" + maxPrice2);// 输出
总价格之和-方式1:Optional[6711000]
总价格之和-方式2:6711000
总价格之和-方式3:6711000
最高售价-方式1:1250000
最高售价-方式2:1250000

3.6 收集(collect)

把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。主要使用java.util.stream.Collectors提供的静态方法。

3.6.1 归集(toList/toSet/toMap)

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。

List<Integer> list = Arrays.asList(1, 3, 5, 7, 6, 8, 9);// toListList<Integer> listNew = list.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());// toSetSet<Integer> set = list.stream().filter(n -> n % 2 == 0).collect(Collectors.toSet());// toMapList<Car> carList = getCars();Map<?, Car> map = carList.stream().filter(car -> car.getPrice() > 500000).collect(Collectors.toMap(Car::getName, car -> car));// key和value均为字段Map<?, String> map2 = carList.stream().collect(Collectors.toMap(Car::getName, Car::getBrand));// 提供了一个取值的方法 (k1,k2) -> k1,如果k1和k2相等,取k1作为keyMap<?, Car> map31 = carList.stream().filter(car -> { "德国".equals(car.getBrand()); return true; }).collect(Collectors.toMap(Car::getName, car -> car, (k1, k2) -> k1));// 将前面的value 和后面的value拼接起来;Map<?, String> map32 = carList.stream().filter(car -> { "德国".equals(car.getBrand()); return true; }).collect(Collectors.toMap(Car::getName, Car::getBrand, (k1, k2) -> k1 + "," + k2));// 重复时将重复key的数据组成集合;Map<?, List<String>> map33 = carList.stream().filter(car -> { "德国".equals(car.getBrand()); return true; }).collect(Collectors.toMap(Car::getName,car -> {List<String> brandList = new ArrayList<>();brandList.add(car.getBrand());return brandList;},(List<String> value1, List<String> value2) -> {value1.addAll(value2);return value1;}));// value为null问题。使用filter过滤掉null值Map<?, String> map4 = carList.stream().filter(car -> car.getBrand() != null).collect(Collectors.toMap(Car::getName, Car::getBrand));System.out.println("List -> List:" + listNew);System.out.println("List -> Set:" + set);System.out.println("List -> Map:" + map);System.out.println("List -> Map:" + map2);System.out.println("List -> Map:" + map31);System.out.println("List -> Map:" + map32);System.out.println("List -> Map:" + map33);System.out.println("List -> Map:" + map4);// 输出
List -> List:[6, 8]
List -> Set:[6, 8]
List -> Map:{E300L 尊贵型=com.lfc.stream.Car@1ddc4ec2, 530Li 尊享型 M运动套装=com.lfc.stream.Car@133314b, ES300h 行政版=com.lfc.stream.Car@b1bc7ed, S500L 4MATIC=com.lfc.stream.Car@7cd84586, 740Li xDrive 行政型 豪华套装=com.lfc.stream.Car@30dae81}
List -> Map:{CT6 28T尊贵型=凯迪拉克, ES260 卓越版=雷克萨斯, E300L 尊贵型=奔驰, XT6 六座四驱豪华型=凯迪拉克, C260L 运动版4MATIC=奔驰, 530Li 尊享型 M运动套装=宝马, ES300h 行政版=雷克萨斯, S500L 4MATIC=奔驰, CT5 28T铂金型=凯迪拉克, ES300h 尊享版=雷克萨斯, 740Li xDrive 行政型 豪华套装=宝马, 325Li M运动曜夜=宝马}
List -> Map:{CT6 28T尊贵型=com.lfc.stream.Car@1b2c6ec2, ES260 卓越版=com.lfc.stream.Car@4edde6e5, E300L 尊贵型=com.lfc.stream.Car@1ddc4ec2, XT6 六座四驱豪华型=com.lfc.stream.Car@70177ecd, C260L 运动版4MATIC=com.lfc.stream.Car@1e80bfe8, 530Li 尊享型 M运动套装=com.lfc.stream.Car@133314b, ES300h 行政版=com.lfc.stream.Car@b1bc7ed, S500L 4MATIC=com.lfc.stream.Car@7cd84586, CT5 28T铂金型=com.lfc.stream.Car@66a29884, ES300h 尊享版=com.lfc.stream.Car@4769b07b, 740Li xDrive 行政型 豪华套装=com.lfc.stream.Car@30dae81, 325Li M运动曜夜=com.lfc.stream.Car@cc34f4d}
List -> Map:{CT6 28T尊贵型=凯迪拉克, ES260 卓越版=雷克萨斯, E300L 尊贵型=奔驰, XT6 六座四驱豪华型=凯迪拉克, C260L 运动版4MATIC=奔驰, 530Li 尊享型 M运动套装=宝马, ES300h 行政版=雷克萨斯, S500L 4MATIC=奔驰, CT5 28T铂金型=凯迪拉克, ES300h 尊享版=雷克萨斯, 740Li xDrive 行政型 豪华套装=宝马, 325Li M运动曜夜=宝马}
List -> Map:{CT6 28T尊贵型=[凯迪拉克], ES260 卓越版=[雷克萨斯], E300L 尊贵型=[奔驰], XT6 六座四驱豪华型=[凯迪拉克], C260L 运动版4MATIC=[奔驰], 530Li 尊享型 M运动套装=[宝马], ES300h 行政版=[雷克萨斯], S500L 4MATIC=[奔驰], CT5 28T铂金型=[凯迪拉克], ES300h 尊享版=[雷克萨斯], 740Li xDrive 行政型 豪华套装=[宝马], 325Li M运动曜夜=[宝马]}
List -> Map:{CT6 28T尊贵型=凯迪拉克, ES260 卓越版=雷克萨斯, E300L 尊贵型=奔驰, XT6 六座四驱豪华型=凯迪拉克, C260L 运动版4MATIC=奔驰, 530Li 尊享型 M运动套装=宝马, ES300h 行政版=雷克萨斯, S500L 4MATIC=奔驰, CT5 28T铂金型=凯迪拉克, ES300h 尊享版=雷克萨斯, 740Li xDrive 行政型 豪华套装=宝马, 325Li M运动曜夜=宝马}

3.6.2 统计(count/averaging)

  • 计数:count
  • 平均值:averagingInt、 averagingLong、 averagingDouble
  • 最值:maxBy、minBy
  • 求和:summingInt、summingLong、summingDouble
  • 统计以上所有:summarizingInt、summarizingLong、summarizingDouble

统计车子数、平均价格、售价总额、最高售价。

List<Car> carList = getCars();// 总数long count = carList.stream().collect(Collectors.counting());// 汽车平均售价Double averagePrice = carList.stream().collect(Collectors.averagingDouble(Car::getPrice));// 最高售价Optional<Integer> maxPrice = carList.stream().map(Car::getPrice).collect(Collectors.maxBy(Integer::compare));// 总售价Integer sum = carList.stream().collect(Collectors.summingInt(Car::getPrice));// 价格所有信息DoubleSummaryStatistics collect = carList.stream().collect(Collectors.summarizingDouble(Car::getPrice));System.out.println("总数:" + count);System.out.println("汽车平均售价:" + averagePrice);System.out.println("最高售价:" + maxPrice);System.out.println("总售价:" + sum);System.out.println("价格所有信息:" + collect);

输出

总数:12
汽车平均售价:559250.0
最高售价:Optional[1250000]
总售价:6711000
价格所有信息:DoubleSummaryStatistics{count=12, sum=6711000.000000, min=306000.000000, average=559250.000000, max=1250000.000000}

3.6.3 分组(partitioningBy/groupingBy)

  • 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
List<Car> carList = getCars();// 将汽车按照售价是否超过50w分区Map<Boolean, List<Car>> part = carList.stream().collect(Collectors.partitioningBy(car -> car.getPrice() > 500000));// 将汽车按照品牌分组Map<String, List<Car>> group = carList.stream().collect(Collectors.groupingBy(Car::getBrand));// 将汽车先按颜色分组,再按照品牌分组Map<String, Map<String, List<Car>>> group2 = carList.stream().collect(Collectors.groupingBy(Car::getColor, Collectors.groupingBy(Car::getBrand)));// 按品牌分组,统计每个组总价格Map<String, Integer> groupByBrand1 = carList.stream().collect(Collectors.groupingBy(Car::getBrand, Collectors.summingInt(Car::getPrice)));// 按品牌分组,统计每个组总条数Map<String, Long> groupByBrand2 = carList.stream().collect(Collectors.groupingBy(Car::getBrand, Collectors.counting()));// 统计每个品牌,每个颜色的数量Map<String, Map<String, Long>> groupByBrand3 = carList.stream().collect(Collectors.groupingBy(Car::getBrand,Collectors.groupingBy(Car::getColor, Collectors.counting())));System.out.println("将汽车按照售价是否超过50w分区: " + part);System.out.println("将汽车按照品牌分组: " + group);System.out.println("将汽车先按颜色分组,再按照品牌分组: " + group2);System.out.println("按品牌分组,统计每个组总价格: " + groupByBrand1);System.out.println("按品牌分组,统计每个组总条数: " + groupByBrand2);System.out.println("统计每个品牌,每个颜色的数量: " + groupByBrand3);

3.6.4 接合(joining)

将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

List<Car> carList = getCars();List<String> distinctBrand = carList.stream().map(car -> car.getBrand()).distinct().collect(Collectors.toList());String brandStr = distinctBrand.stream().collect(Collectors.joining("-"));System.out.println("拼接去重后的品牌名称:" + brandStr);// 输出
拼接去重后的品牌名称:宝马-奔驰-雷克萨斯-凯迪拉克

3.6.5 归约(reducing)

Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

List<Car> carList = getCars();// 每辆车减去购置税25000后的售价总和Integer sum = carList.stream().collect(Collectors.reducing(0, Car::getPrice, (x, y) -> (x + y - 25000)));System.out.println("每辆车减去购置税25000后的售价总和:" + sum);// Stream的reduce方法Optional<Integer> sum2 = carList.stream().map(Car::getPrice).reduce(Integer::sum);System.out.println("售价总和:" + sum2.get());

3.7 排序(sorted)

可以类比于MySQL的order by

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):Comparator排序器自定义排序
List<Car> carList = getCars();// 按售价升序List<String> priceAscList = carList.stream().sorted(Comparator.comparing(Car::getPrice)).map(Car::getName).collect(Collectors.toList());// 按售价降序List<String> priceDescList = carList.stream().sorted(Comparator.comparing(Car::getPrice).reversed()).map(Car::getName).collect(Collectors.toList());// 先车寿命再按售价升序List<String> lifePriceAscList = carList.stream().sorted(Comparator.comparing(Car::getLife).thenComparing(Car::getPrice)).map(Car::getName).collect(Collectors.toList());// 先按车寿命再按售价降序(自定义排序)List<String> lifePriceDeseBySelfList = carList.stream().sorted((car1, car2) -> {if (car1.getLife() == car2.getLife()) {return car2.getPrice() - car1.getPrice();} else {return car2.getLife() - car1.getLife();}}).map(Car::getName).collect(Collectors.toList());System.out.println("按售价升序: " + priceAscList);System.out.println("按售价降序: " + priceDescList);System.out.println("先车寿命再按售价升序: " + lifePriceAscList);System.out.println("先按车寿命再按售价降序(自定义排序): " + lifePriceDeseBySelfList);

3.8 提取/组合 【终端操作】

流也可以进行合并、去重、限制、跳过等操作。


String[] arr1 = {"p", "y", "t", "h", "o", "n"};String[] arr2 = {"J", "a", "v", "a", "e", "e"};Stream<String> stream1 = Stream.of(arr1);Stream<String> stream2 = Stream.of(arr2);// 合并(concat)和去重(distinct)  类比 MySQL 的concat 和 distinctList<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());// 限制获取多少元素(limit) 类比 MySQL 的limitList<Integer> collect = Stream.iterate(1, i -> i + 2).limit(5).collect(Collectors.toList());// skip 跳过前n个元素 类比 MySQL 的offsetList<Integer> collect2 = Stream.iterate(1, i -> i + 2).skip(2).limit(5).collect(Collectors.toList());System.out.println("合并和去重:" + newList);System.out.println("限制:" + collect);System.out.println("跳过:" + collect2);

输出:

合并和去重:[p, y, t, h, o, n, J, a, v, e]
限制:[1, 3, 5, 7, 9]
跳过:[5, 7, 9, 11, 13]

4. 总结

Java8的Stream API非常强大,其中的一些操作可与MySQL相关的指令类比学习。阿里巴巴规范说明里联表一般不要超过3个,那么我们可以通过简单的查询然后再在程序进行Stream计算,降低SQL的复杂度,为后期的系统庞大之后的分库分表做准备。

理论部分参考文章(两位写的很好):
云深i不知处
三分恶

Java8 Stream-深入理解筛选、归约、分组、聚合相关推荐

  1. Java8 Stream详解~筛选:filter

    筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作. 「案例一:筛选出Integer集合中大于7的元素,并打印出来」 public class StreamTest {publ ...

  2. Java8 Stream:两万字博文教你玩转集合的筛选、归约、分组、聚合

    目录 一.Stream概述 二.Stream 的创建 三.Stream 的中间操作 3.1.筛选(filter/distinct) 3.2.切片(limit/skip) 3.3.映射(map/mapT ...

  3. Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

    Java8 Stream 1 Stream概述 2 Stream的创建 3 Stream的使用 案例使用的员工类 3.1 遍历/匹配(foreach/find/match) 3.2 筛选(filter ...

  4. Java8 Stream 流的创建、筛选、映射、排序、归约、分组、聚合、提取与组合、收集、接合、foreach遍历

    目录 一  了解Stream 1 Stream概述 那么什么是Stream? Stream可以由数组或集合创建 Stream有几个特性: Stream流的起始操作 2 Stream的创建----Str ...

  5. stream 多个字段分组_Python Pandas对Excel数据的分组聚合和数据透视

    使用Excel进行商业数据分析的时候,最重要的就是两个手段就是vlookup函数和数据透视表.本章就讲解一下与数据透视功能相关的分组聚合和数据透视.其实分组聚合和数据透视两者基本是等价的,但由于使用的 ...

  6. 最详细最直观的>>>理解Pandas分组聚合和透视图标

    写在前面: 突然打开excel看到透视图表, 瞬间理解了pandas中的分组聚合和透视图标的内容.分享给大家,希望给在学习路上的伙伴们少点疑虑. 分组聚合 首先, 创建我们需要的数据 df = pd. ...

  7. 阿拉丁统计 2.0丨筛选?分组?聚合能力?「高级分析」精细化数据统计管理

    前两期,我们分享了阿拉丁统计 2.0 的「开源 SDK」和「指标商店」,后台收到了很多粉丝的咨询与试用申请.本期我们就从"事件分析"着手,开启第三个话题--「高级分析」. 在日常工 ...

  8. 20个实例玩转Java8 Stream

    20个实例玩转Java8 Stream 20个实例玩转Java8 Stream Stream概述 Stream的创建 stream和parallelStream的简单区分 stream的使用 遍历/匹 ...

  9. 【Java8 Stream】:探秘Stream实现的核心:Collector,模拟Stream的实现

    目录 前言 Collector的基础知识 Collector源码 一个简单的Collector实现类 模拟Stream,使用Collector实现一个简单的年龄计算 Stream的用法可以参考下文: ...

最新文章

  1. 算法导论Java实现-随机化数组的两种方式(5.3章节)
  2. java sessionstate_在Java Web开发中自定义Session
  3. 一张图说明Linux启动过程
  4. 35岁以前把下面十件事做好
  5. codeforces gym-101745 D-Stamp Stamp Stamp动态规划
  6. 统计文件中有多少个单词amp;c语言实现
  7. 如何用js获取浏览器URL中查询字符串的参数
  8. Perl文档操作选项
  9. 腾讯视频下载转mp4_腾讯视频如何上传自己的视频
  10. VS设置程序启动权限为管理员权限
  11. js中sort()方法的用法,参数以及排序原理
  12. MTK 驱动(60)---Audio驱动开发之音频链路
  13. NFS服务安装与配置方案
  14. POJ 1236 Network of Schools (校园网)
  15. 刘意-Java基础视频(基础部分)笔记(一)
  16. Drool学习记录(二) Kie Session、Truth maintenance
  17. 代理工具及使用技巧Proxy Hunter
  18. 数列极限:数列极限的性质
  19. 微信小程序实现上传图片的功能
  20. cmd强制删除文件夹

热门文章

  1. [18考研]专业课答题纸 大揭秘!免费下载啦~
  2. net-java-php-python-网上书店管理系统设计计算机毕业设计程序
  3. 四川教师计算机培训,四川省中小学教师信息技术应用能力提升培训研修计划.doc...
  4. 使用python进行ABAQUS的二次开发的简要说明(by Young 2017.06.27)
  5. 渗透测试之信息收集(超完整版)
  6. 卷积神经网络学习二:tinny_cnn程序试运行
  7. 机试算法讲解: 第11题 贪心之猫鼠大战
  8. python 卷积神经网络猫狗大战_卷积神经网络入门(1) 识别猫狗
  9. 如何在GitHub中上传图片
  10. 计算机辅助药物合成功能,计算机辅助药物设计在药物合成中的应用_郑彦.pdf