java流操作

发布时间 2023-09-27 08:17:01作者: 五官一体即忢

1 认识Stream流

在Java中我们对数组和集合的操作一定是非常多的,几乎每个程序都会制造和处理数组或者集合。在Java 8之前我们处理这些数据时需要迭代、需要判断并且需要写具体的算法才能获取到想要的结果。有了流之后只需要表达想要什么,最终就会得到想要的结果,不必去担心怎么去显示的实现这些算法。

如果要去大量元素,并且想提高性能,就需要做并行处理,利用多核架构。这就需要多线程代码,但多线程的代码是很复杂的,稍有不甚就会带来很多bug。

在Java 8中引入的流就帮我们解决了这些问题,让我们的代码更加的优雅。

1.1 什么是流

Stream流是Java API的新成员,它允许以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。其实完全可以把它看成一个遍历数据集的高级迭代器。而且还可以并行的去处理数据集,无需写任何多线程代码。

下面我们用一个例子来演示Stream流:
1、创建一个水果类,name表示水果名称,weight表示水果重量。

public class Fruits {/*** 名称 eg: 香蕉、苹果、橙子*/private String name;/*** 重量 单位:克(g)*/private Integer weight;public Fruits(String name, Integer weight) {this.name = name;this.weight = weight;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getWeight() {return weight;}public void setWeight(Integer weight) {this.weight = weight;}@Overridepublic String toString() {return "Fruits{" +"name='" + name + '\'' +", weight=" + weight +'}';}
}

2、创建一个list集合存放一些水果类,可以理解这个list集合为果篮。

List<Fruits> list = new ArrayList<>();
list.add(new Fruits("苹果",132));
list.add(new Fruits("苹果",145));
list.add(new Fruits("苹果",120));
list.add(new Fruits("苹果",190));
list.add(new Fruits("香蕉",90));
list.add(new Fruits("香蕉",50));
list.add(new Fruits("香蕉",100));
list.add(new Fruits("西瓜",2000));

3、Java 8之前做如下操作需要这样写代码

List<Fruits> list = getList();
// 1.累加果篮中所有水果的重量
int totalWeight = 0;
for (Fruits fruits : list) {totalWeight += fruits.getWeight();
}
System.out.println(totalWeight);
// 2.根据重量对水果排序,使用匿名类
Collections.sort(list, new Comparator<Fruits>() {@Overridepublic int compare(Fruits o1, Fruits o2) {return o1.getWeight().compareTo(o2.getWeight());}
});
System.out.println(list);
// 3.获取排序后的水果名称
List<String> fruitsName = new ArrayList<>();
for (Fruits fruits : list) {fruitsName.add(fruits.getName());
}
System.out.println(fruitsName);

4、Java 8中我们做如下操作可以这样写代码

List<Fruits> list = getList();
List<String> fruitsName = list.stream().filter(s -> s.getWeight() > 120) // 获取重量大于120g的水果.sorted(Comparator.comparing(Fruits::getWeight)) // 按重量从小到大排序.map(Fruits::getName) // 获取水果名称.collect(Collectors.toList());// 生成新的列表// [苹果, 火龙果, 芒果, 西瓜]
System.out.println(fruitsName);

5、如果我们想使用多核架构并发来执行这段代码,只需要将stream改为parallelStream

List<Fruits> list = getList();
List<String> fruitsName = list.parallelStream().filter(s -> s.getWeight() > 120) // 获取重量大于120g的水果.sorted(Comparator.comparing(Fruits::getWeight)) // 按重量从小到大排序.map(Fruits::getName) // 获取水果名称.collect(Collectors.toList());// 生成新的列表// [苹果, 火龙果, 芒果, 西瓜]
System.out.println(fruitsName);

总结:

从上面的代码我们发现Stream流让代码变得更简单。其中代码是以声明性的方式写的,只需要说明想要完成什么即可,不需要去写如何实现的代码。而且还可以将几个基础操作链接起来,类似流水线一样的去处理数据。

Stream

1.2 流与集合

集合是一个内存中的数据结构,它包含数据结构中目前所有的值。流则是概念上固定的数据结构(不能添加或删除元素),其元素是按需获取的,使用一个获取一个。

1.2.1 流只能遍历一次

流和迭代器非常的类似,只能遍历一次。遍历完之后,这个流就已经被消费掉了。要先使用的话只能怪从原始数据源那里再获取一个新的流。

List<Fruits> list = getList();
Stream<Fruits> stream = list.stream();
stream.forEach(fruits -> System.out.println(fruits)); // 打印流中元素
// 再次打印则会报错
// java.lang.IllegalStateException: stream has already been operated upon or closed
stream.forEach(s -> System.out.println(s));

1.2.2 外部迭代和内部迭代

对集合使用for,for-each迭代都属于外部迭代,我们需要手动来完成迭代逻辑。而流的迭代属于内部迭代,不需要我们来实现流的迭代逻辑,只管完成逻辑代码就好,流已经替我们把迭代做了。

List<Fruits> list = getList();
// 外部迭代
for (Fruits fruits : list) {System.out.println(fruits.getName());
}// 内部迭代
list.stream().forEach(fruits -> System.out.println(fruits.getName()));

1.3 流操作

在java.util.stream.Stream中的Stream接口中定义了许多操作。我们将这些操作分为两类:中间操作和终端操作。
中间操作和终端操作

1.3.1 中间操作

中间操作都会返回一个符合条件的流,可以紧接着再执行中间操作或终端操作。例如上面我们在falter操作后紧接着执行sorted操作。

操作类型返回类型操作参数函数描述符
filter 中间 Stream Predicate T -> boolean
map 中间 Stream Function<T, R> T -> R
limit 中间 Stream    
sorted 中间 Stream Comparator (T, T) -> int
distinct 中间 Stream    

1.3.2 终端操作

终端操作会生成流的结果,具体的结果根据我们声明的方法来决定。例如我们上面我们通过collect来生成一个List列表。也可以使用forEach来直接打印结果。终端操作后就结束了,不会生成新的流所以也不能再接后续的操作。

操作类型目的
forEach 终端 消费流中的每个元素并对其应用Lambda。返回void。
count 终端 返回流中元素个数。这一操作返回long。
collect 终端 把流归约成一个集合,比如List、Map甚至是Integer。

1.3.3 使用流

流的使用一般包括三件事:

  • 一个数据源(如集合)来执行一个查询
  • 一个中间操作链,形成一条流的流水线
  • 一个终端操作,执行流水线,并能生成结果

2 学会使用Stream流

2.1 筛选和切片

2.1.1 用谓词筛选

Streams接口支持filter方法,此方法接受一个谓词作为参数,返回一个符合谓词的元素的流。

// 筛选重量大于120g的水果
List<Fruits> list = getList();
list.stream().filter(s -> s.getWeight() > 120).forEach(System.out::println);

2.1.2 筛选各异的元素

使用distinct方法对流中的元素去重(根据元素的hashCode和equals方法)。

// 对这个集合中的元素去重
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().distinct().forEach(System.out::println);

2.1.3 截短流

使用limit(n)方法,可以返回一个不超过给定长度的流。

// 筛选重量大于120g的水果,选取前三个
List<Fruits> list = getList();
list.stream().filter(s -> s.getWeight() > 120).limit(3).forEach(System.out::println);

2.1.4 跳过元素

skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。

// 筛选重量大于120g的水果,跳过前三个,返回剩下的
List<Fruits> list = getList();
list.stream().filter(s -> s.getWeight() > 120).skip(3).forEach(System.out::println);

2.2 映射

一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里,你可以从表中选择一列。Stream API也通过map和flatMap方法提供了类似的工具。

2.2.1 map方法

map方法接受一个Function<T, R>类型的函数作为参数,流中的元素T经过这个流会映射为一个新元素R。(这里的映射是转换为新元素,是创建一个新元素而不是去修改原有的元素)。

// 获取流中每个元素的长度并打印
List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
words.stream().map(String::length).forEach(System.out::println);

2.2.2 flatMap方法

我们拿一个例子来演示map方法和flatMap方法的不同:我们对集合[“Java 8”, “Lambdas”, “In”, “Action”]进行处理,返回这个列表中涉及到的所有字母,不可以重复。
正确的结果应为:[ ‘J’ , ‘a’ , ‘v’ , ’ ’ , ‘8’ , ‘L’ , ‘m’ , ‘b’ , ‘d’ , ‘s’ , ‘I’ , ‘n’ , ‘A’ , ‘c’ , ‘t’ , ‘i’ , ‘o’]

第一反应我们肯定使用如下代码来解决:

words.stream().map(s -> s.split("")).distinct().forEach(System.out::println);

返回结果:
在这里插入图片描述
这样的结果是因为我们给map输入的为Stream<String>流,split方法返回的是一个数组,所以返回的类型为Stream<String[]>流,打印数组当然就是这样的结果了。

我们可以将Stream<String[]>流使用方法Arrays.toString将数组转换为字符串,然后打印来观察结果:

words.stream().map(s -> s.split("")).distinct().map(Arrays::toString).forEach(System.out::println);

返回结果:
撒反对
我们观察到这完全不是想要的结果。下面用一张图来解释为什么是这样的结果。

jiwshi
我们发现上面的distinct的去重其实是对String[]进行去重的。

下面我们使用flatMap(Arrays::stream)来将Stream<String[]>流转换为Stream<String>来实现想要的结果

List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<String> list = words.stream().map(s -> s.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());System.out.println(list);

返回结果:
在这里插入图片描述
这正是我们想要的结果,我们也用一张图来解释一下这个的原因:
在这里插入图e片描述
一言以蔽之,flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流.

2.3 查找和匹配

2.3.1 检查谓词是否至少匹配一个元素

使用anyMatch方法可以判断流中是否存在一个这样的元素。

// 判断流中是否存在苹果
List<Fruits> list = getList();
boolean b = list.stream().anyMatch(s -> "苹果".equals(s.getName()));
System.out.println(b);

2.3.2 检查谓词是否匹配所有元素

allMatch方法判断流中的所有元素是否都匹配,noneMatch方法判断流中所有的元素都不匹配。这两个方法都用到了“短路”,及发现一个不符合则不再继续进行判断了。

// 判断流中元素重量是否都大于10g
List<Fruits> list = getList();
boolean b = list.stream().allMatch(s -> s.getWeight() > 10);
System.out.println(b);
// 判断流中元素重量是否都不大于(小于)10g
List<Fruits> list = getList();
boolean b = list.stream().noneMatch(s -> s.getWeight() > 10);
System.out.println(b);

2.3.3 查找元素

使用findAny方法可以查找流中符合条件的元素,返回符合条件的任意一个元素。此方法也利用了“短路"操作,找到一个元素即立即结束。

// 找到重量大于100g的任意一元素
List<Fruits> list = getList();
Optional<Fruits> any = list.stream().filter(s -> s.getWeight() > 100).findAny();
System.out.println(any.get());

2.3.4 查找第一个元素

使用findFirst方法可以返回符合条件的第一个元素,findAny是任意一个元素,fingFirst是返回第一个元素,这里需要的流为顺序流(如List或者其他排好序的流)。

// 找到重量大于100g的第一个元素
List<Fruits> list = getList();
Optional<Fruits> any = list.stream().filter(s -> s.getWeight() > 100).findFirst();
System.out.println(any.get());

2.4 归约

到目前为止,你见到过的终端操作都是返回一个boolean(allMatch之类的)、void(forEach)或Optional对象(findAny等)。你也见过了使用collect来将流中的所有元素组合成一个List。

归约就是对我们得到的结果进行统计,例如得到所有水果的重量和,得到重量最高的那个水果等等。

2.4.1 元素求和

使用reduce方法可以对流中所有元素求和

// 对所有元素的重量求和
List<Fruits> list = getList();
Integer reduce = list.stream().map(Fruits::getWeight).reduce(0, Integer::sum);
System.out.println(reduce);

上面reduce方法第一个参数是默认值,可以不填,不填的话会返回一个Optional对象,考虑流中没有任何元素的情况下reduce方法不能求和,所以将结果包含在Optional对象中。

// 对所有元素的重量求和
List<Fruits> list = getList();
Optional<Integer> reduce = list.stream().map(Fruits::getWeight).reduce( Integer::sum);
System.out.println(reduce.get());

2.4.2 最大值和最小值

可以使用reduce来计算流中的最大值或者最小值

// 得到所有元素中最大的重量的值
List<Fruits> list = getList();
Optional<Integer> reduce = list.stream().map(Fruits::getWeight).reduce( Integer::max);
System.out.println(reduce.get());
// 得到所有元素中最小的重量的值
List<Fruits> list = getList();
Optional<Integer> reduce = list.stream().map(Fruits::getWeight).reduce( Integer::min);
System.out.println(reduce.get());

2.5 数值流

Java 8引入了三个原始类型特化流接口来解决这个问题:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max。

2.5.1 映射到数值流

IntStream中含有特定方法执行归约操作,例如sum方法还有max、min、average方法等。sum方法默认返回的是0,而max、min和average方法返回的为OptionalInt等对应的方法。

// 得到所有元素重量和
List<Fruits> list = getList();
Integer sum = list.stream().map(Fruits::getWeight).reduce(0, Integer::sum); // 这里暗含了装箱的成本
IntStream intStream = list.stream().mapToInt(Fruits::getWeight); // 转换为IntStream流
int sum1 = intStream.sum();// IntStream流含有特定的sum方法可以直接得出和
System.out.println(sum);
System.out.println(sum1);

2.5.2 转换回对象流

List<Fruits> list = getList();
Integer sum = list.stream().map(Fruits::getWeight).reduce(0, Integer::sum);
IntStream intStream = list.stream().mapToInt(Fruits::getWeight);
Stream<Integer> boxed = intStream.boxed(); // 转换会对象流

数值范围

IntStream和LongStream中含有2个方法range和rangeClosed来帮助生成范围内的数字。他们的参数都是第一个为起始值,第二个为结束值,但range是不包含结束值的,rangeClosed是包含结束值的。

int sum = IntStream.range(0, 100).filter(s -> s % 2 == 0).sum();
System.out.println(sum);

2.6 构建流

2.6.1 由值创建流

使用静态方法Stream.of,通过显式值创建一个流。

Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);

可以使用Stream.empty()来创建一个空流。

2.6.2 由数组创建流

可以使用Arrays。stream(数组)将一个数组转换为流。

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
System.out.println(sum);

2.6.3 由文件生成流

Java中用于处理文件等I/O操作的NIO API(非阻塞 I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法是Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

        long uniqueWords = 0;try(Stream<String> lines =Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))).distinct().count();}catch(IOException e){}

2.6.4 由函数生成流:创建无限流

Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由iterate和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说,应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。
1、迭代

Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);

iterate方法接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator类型)。这里,我们使用Lambda n -> n + 2,返回的是前一个元素加上2。

2、生成
与iterate方法类似,generate方法也可让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier类型的Lambda提供新的值。

Stream.generate(Math::random).limit(5).forEach(System.out::println);

请注意,因为你处理的是一个无限流,所以必须使用limit操作来显式限制它的大小;否则,终端操作将永远计算下去。

3 利用Stream流来处理更为复杂的数据处理查询

前面我们已经知道可以使用collect(Collectors.toList())来讲流中的元素结合成一个List。其实collect和reduce一样都是一个归约操作。可以接受一个参数将流中的元素汇总为一个结果。
reduce是接受一个BinaryOperator作为谓词,collect是接受一个Collector接口作为谓词。

3.1 collect可以做什么

还是利用我们之前的果篮List,我们可以:

  • 按不同的水果进行分类,并获
  • 取该水果的总重量。 将水果分为两类,重的和轻的并返回他们的总重量。
  • 可以进行多级分组,先按不同水果分类,再将同一种水果分为重得和轻的。

重新定义果篮集合

List<Fruits> list = new ArrayList<>();
list.add(new Fruits("苹果",132));
list.add(new Fruits("火龙果",145));
list.add(new Fruits("火龙果",120));
list.add(new Fruits("苹果",190));
list.add(new Fruits("火龙果",90));
list.add(new Fruits("苹果",50));
list.add(new Fruits("火龙果",100));
list.add(new Fruits("火龙果",200));

将果篮中的不同水果区分出来

// java 8之前区分
Map<String,List<Fruits>> map = new HashMap<>();
for (Fruits fruits : list) {if (map.containsKey(fruits.getName())) {List<Fruits> fruitsList = map.get(fruits.getName());fruitsList.add(fruits);map.put(fruits.getName(),fruitsList);} else {List<Fruits> fruitsList = new ArrayList<>();fruitsList.add(fruits);map.put(fruits.getName(), fruitsList);}
}
// java 8中使用collect区分
Map<String, List<Fruits>> collectMap = list.stream().collect(Collectors.groupingBy(Fruits::getName));

从上面的代码中我们看到了使用collect来做区分的便利性,我们只需要表达出想要什么,完全不需要去考虑该如何做。

3.2 使用collect进行归约和汇总

Collectors接口为我们已经定义了许多有用的默认方法,我们可以直接使用。

1、统计果篮(List)中水果的数量

// 使用collect进行统计
Long collect = list.stream().collect(Collectors.counting());
// 可以直接使用count来统计
long count = list.stream().count();

2、查询果篮中最重和最轻的水果
需要利用Collectors.maxBy方法,参数为Comparator比较器。

// 获取最重的水果
List<Fruits> list = getList();
Comparator<Fruits> fruitsComparator = Comparator.comparingInt(Fruits::getWeight);
Optional<Fruits> maxFruits = list.stream().collect(Collectors.maxBy(fruitsComparator));
System.out.println(maxFruits.get());
// 获取最轻的水果
List<Fruits> list = getList();
Comparator<Fruits> fruitsComparator = Comparator.comparingInt(Fruits::getWeight);
Optional<Fruits> maxFruits = list.stream().collect(Collectors.minBy(fruitsComparator));
System.out.println(maxFruits.get());

3、求果篮中水果的全部重量,平均重量,最大值,最小值,数量信息
Collectors类提供了工厂方法Collectors.summingInt来求和,summingInt将一个对象映射为求和所需的int函数,传递给collect方法执行来获取最终结果。

// 如果流为空则返回0,默认值为0
Integer collect = list.stream().collect(Collectors.summingInt(Fruits::getWeight));
System.out.println(collect);

类似的方法还有Collectors.summingLong和Collectors.summingDouble,他们作用都是一样的,用于字段为long和double的情况。

Collectors类还提供了一个更为强大的方法summarizingInt,他可以帮我求出这个流的总和、平均值、最大值和最小值,会将结果存放在作IntSummaryStatistics的类里,获取我们想要的结果即可。

IntSummaryStatistics fruitsTotal = list.stream().collect(Collectors.summarizingInt(Fruits::getWeight));
System.out.println(fruitsTotal.getMax());
System.out.println(fruitsTotal.getSum());
System.out.println(fruitsTotal.getCount());
System.out.println(fruitsTotal.getAverage());
System.out.println(fruitsTotal.getMin());

都有对应Long和Double的方法。

4、连接字符串
joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。

String collect = list.stream().map(Fruits::getName).collect(Collectors.joining());
System.out.println(collect);

该字符串的可读性并不好。joining工厂方法有一个重载版本可以接受元素之间的分界符

String collect = list.stream().map(Fruits::getName).collect(Collectors.joining(","));
System.out.println(collect);

上面我们见到的收集器,其实都可以使用Collectors.reducing方法来完成,之前写的方法都是为了方便。

// 对果篮中水果重量求和,第一个参数为初始值,第二参数为转换函数,第三个参数BinaryOperator将两个元素累加
Integer collect = list.stream().collect(Collectors.reducing(0, Fruits::getWeight, (i, j) -> i + j));
System.out.println(collect);
// 找到重的水果
Optional<Fruits> collect = list.stream().collect(Collectors.reducing((i, j) -> i.getWeight() > j.getWeight() ? i : j));
System.out.println(collect.get());

3.3 使用collect进行分组

分组就是将不同类型的物品分别放到不同的组,例如在果篮(List)中可以将苹果分为一个组,火龙果分为另一个组。

// 将不同名称的元素分为不同的组
Map<String, List<Fruits>> collect = list.stream().collect(Collectors.groupingBy(Fruits::getName));
System.out.println(collect);

上面我们根据元素的名称来分组,相同名称的分为同一个组,使用Collectors.groupingBy可以帮我们完成这样的操作,最终返回的是Map<String, List>类型。

也可以根据重量来分组,小于70g的分为一组,大于等于70小于150g的分为一组,大于等于150g的分为一组。

// 将不同重量的元素分为不同的组
Map<String, List<Fruits>> collect = list.stream().collect(Collectors.groupingBy(fruits -> {if (fruits.getWeight() < 50) {return "小于50g";} else if (fruits.getWeight() >= 50 && fruits.getWeight() < 150) {return "大于等于50g小于150g";} else {return "大于等于150g";}
}));
System.out.println(collect);

多级分组

多级分组就是可以先按名称分组再按重量分组,可以利用Collectors.groupingBy的另一个版本,他可以接受2个参数,第二个参数传递另一个分组方式来进行多级分组。

Map<String, Map<String, List<Fruits>>> collect = list.stream().collect(Collectors.groupingBy(Fruits::getName, Collectors.groupingBy(fruits -> {if (fruits.getWeight() < 50) {return "小于50g";} else if (fruits.getWeight() >= 50 && fruits.getWeight() < 150) {return "大于等于50g小于150g";} else {return "大于等于150g";}
})));
System.out.println(collect);

第二个参数其实可以不是Collectors.groupingBy方法的,也可以是Collectors.counting方法来收集数量。

Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(Fruits::getName, Collectors.counting()));
System.out.println(collect);

还可以过去不同元素中最大的元素是哪个

Map<String, Optional<Fruits>> collect = list.stream().collect(Collectors.groupingBy(Fruits::getName, Collectors.maxBy(Comparator.comparingInt(Fruits::getWeight))));
System.out.println(collect);

我们发现元素都是使用Optional类包起来的,这样防止没有元素也不会产生异常的问题。

因为分组操作的Map结果中的每个值上包装的Optional没什么用,所以你可能想要把它们去掉。要做到这一点,或者更一般地来说,把收集器返回的结果转换为另一种类型,你可以使用Collectors.collectingAndThen工厂方法返回的收集器。

Map<String, Fruits> collect = list.stream().collect(Collectors.groupingBy(Fruits::getName,Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Fruits::getWeight)), Optional::get)));
System.out.println(collect);

3.4 使用collect来进行分区

分区就是分为2个区,一个true一个false,例如按重量分,小于100g的为一个区,其他的为另一个区。

Map<Boolean, List<Fruits>> collect = list.stream().collect(Collectors.partitioningBy(fruits -> fruits.getWeight() < 100));
System.out.println(collect);

上面返回的集合key分别为true和false。

将分区和分组结合起来使用,先将果篮分为小于100g的一个区和其他区,再按名字分组。

Map<Boolean, Map<String, List<Fruits>>> collect = list.stream().collect(Collectors.partitioningBy(fruits -> fruits.getWeight() < 100, Collectors.groupingBy(Fruits::getName)));
System.out.println(collect);
 
文章来源:https://blog.csdn.net/qq_46307070/article/details/129181175