Java 8 Stream 之Collectors 类

Collectors 类的静态工厂方法

工厂方法 返回类型 作用
toList List<T> 把流中所有项目收集到一个 List
toSet Set<T> 把流中所有项目收集到一个 Set,删除重复项
toCollection Collection<T> 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new)
counting Long 计算流中元素的个数
sumInt Integer 对流中项目的一个整数属性求和
averagingInt Double 计算流中项目 Integer 属性的平均值
summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值
joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", "))
maxBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()
minBy Optional<T> 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()
reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingBy Map<K, List<T>> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键
partitioningBy Map<Boolean,List<T>> 根据对流中每个项目应用谓词的结果来对项目进行分区

Collectors

在Collectors类中有所有的预定义实现。一般常见做法是使用下面的静态导入来提高可读性:

import static java.util.stream.Collectors.*;

 或者单个导入你选择的collectors:

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

下面所有示例将使用以下演示数据集合

List<String> givenList = Arrays.asList("a", "bb", "ccc", "dd");

Collectors.toList()

ToList 方法可以收集所有流中元素至List实例。需要注意的是,使用该方法不能假设任何List的特定实现,如果你想指定List实现,需要使用toCollection 代替.下面示例创建一个流实例表示元素序列,然后收集至List实例对象中。

List<String> result = givenList.stream().collect(toList());

Collectors.toSet()

ToSet 方法可以收集所有流中元素至Set 实例.需要注意的是,使用该方法不能假设任何Set的特定实现,如果你想指定Set实现,需要使用toCollection 代替.下面示例创建一个流实例表示元素序列,然后收集至Set例对象中

Set<String> result = givenList.stream().collect(toSet());

Set不包含重复元素。如果集合中有重复元素,则set中仅保留一个:

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
Set<String> result = listWithDuplicates.stream().collect(toSet());
assertThat(result).hasSize(4);

Collectors.toCollection()

你可能已经注意到,使用toSet 和 toList收集器,不能指定其实现。如果想使用特定实现,需要使用toCollection收集器并提供特定集合实现。下面示例创建一个流实例表示元素序列,然后收集至LinkedList例对象中

List<String> result = givenList.stream().collect(toCollection(LinkedList::new))

注意这里不能使用不可变集合实现。通过自定义Collector实现或使用collectingAndThen。

Collectors.toMap()

ToMap 收集器用于收集流元素至 Map 实例,实现该功能需要提供两个函数:

  • keyMapper
  • valueMapper

keyMapper 用于从流元素中抽取map 的key,valueMapper抽取与可以关联的value。
下面示例收集流元素至Map中,存储字符串作为key,其长度作为value:

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length))

Function.identity() 是一个预定义的返回接收参数的快捷函数。
如果我们集合中包含重复元素会怎么样?与toSet相反,toMap不能过滤重复元素。这个比较好理解————其如何确定key关联那个value?

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
assertThatThrownBy(() -> {
    listWithDuplicates.stream().collect(toMap(Function.identity(), String::length));
}).isInstanceOf(IllegalStateException.class);

我们看到,toMap甚至不判断值是否相等,如果key重复,立刻抛出IllegalStateException异常。当key冲突时,我们应该使用toMap的另一个重载方法:

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length, (item, identicalItem) -> item));

第三个参数是二元操作,我们可以指定如何处理key冲突。本例中我们可以任意选择一个,因为两者的长度始终一样。

Collectors.collectingAndThen()

CollectingAndThen 是一个特殊收集器,其可以收集完成后再结果上执行另外动作。下面收集流元素至List示例,然后转换结果为ImmutableList实例:

List<String> result = givenList.stream()
  .collect(collectingAndThen(toList(), ImmutableList::copyOf))

Collectors.joining()

Joining 收集器可以用于连接字符串流Stream中的元素。示例如下:

String result = givenList.stream()
  .collect(joining());

结果如下:

"abbcccdd"

还可以指定分隔符、前缀和后缀:

String result = givenList.stream()
  .collect(joining(" "));

结果如下:

"a bb ccc dd"

再看一个示例:

String result = givenList.stream()
  .collect(joining(" ", "PRE-", "-POST"));

结果为:

"PRE-a bb ccc dd-POST"

Collectors.counting()

Counting 是一个简单收集器,返回元素数量:

Long result = givenList.stream()
  .collect(counting());

Collectors.summarizingDouble/Long/Int()

SummarizingDouble/Long/Int 收集器返回流中抽取的数值元素的统计结果类型。下面示例获取字符串长度信息:

DoubleSummaryStatistics result = givenList.stream()
  .collect(summarizingDouble(String::length));

下面测试代码执行通过:

assertThat(result.getAverage()).isEqualTo(2);
assertThat(result.getCount()).isEqualTo(4);
assertThat(result.getMax()).isEqualTo(3);
assertThat(result.getMin()).isEqualTo(1);
assertThat(result.getSum()).isEqualTo(8);

Collectors.averagingDouble/Long/Int()

AveragingDouble/Long/Int 是返回流中抽取元素的平均值,下面示例计算字符串长度平均值:

Double result = givenList.stream()
  .collect(averagingDouble(String::length));

Collectors.summingDouble/Long/Int()

SummingDouble/Long/Int 返回抽取元素之和,下面计算所有字符串长度之和:

Double result = givenList.stream()
  .collect(summingDouble(String::length));

Collectors.maxBy()/minBy()

MaxBy/MinBy 收集器根据提供的 Comparator 实例,返回流中最大和最小元素,下面计算最大元素:

Optional<String> result = givenList.stream()
  .collect(maxBy(Comparator.naturalOrder()));

需要注意的是,其结果为Optional类型,其强制用户考虑空集合情况

Collectors.groupingBy()

分组收集器用于根据属性对元素进行分组并存储在Map实例中,下面示例根据字符串长度进行分组,并把结果存储在Set中:

Map<Integer, Set<String>> result = givenList.stream()
  .collect(groupingBy(String::length, toSet()));

测试代码如下:

assertThat(result)
  .containsEntry(1, newHashSet("a"))
  .containsEntry(2, newHashSet("bb", "dd"))
  .containsEntry(3, newHashSet("ccc"));

第二个参数是一个集合,我们开使用任何集合实现。

Collectors.partitioningBy()

PartitioningBy 是一个特殊分组收集器,依据 Predicate 实例收集流元素至Map中,存储 Boolean 值作为key,值为集合。 “true” 键对应值为已匹配元素的集合,“false” 键为非匹配元素集合。示例代码如下:

Map<Boolean, List<String>> result = givenList.stream()
  .collect(partitioningBy(s -> s.length() > 2))

结果Map如下:

{false=["a", "bb", "dd"], true=["ccc"]}

Collectors.teeing()

根据目前我们已经学习的内容求最大值和最小值:

List<Integer> numbers = Arrays.asList(42, 4, 2, 24);
Optional<Integer> min = numbers.stream().collect(minBy(Integer::compareTo));
Optional<Integer> max = numbers.stream().collect(maxBy(Integer::compareTo));
// do something useful with min and max

这里,我们使用两个不用的收集器,然后合并集合去实现相应业务。在Java 12之前,为了实现这样功能必须对流执行两次操作,把中间结果存储至临时对象中,最后合并返回。
幸运的是,java 12 提供了内置收集器帮助我们处理这些步骤:我们仅需提供两个收集器和合并函数。
因为新的收集器tee对流执行两个不用方向的操作,故称为T收集器:

numbers.stream().collect(teeing(
  minBy(Integer::compareTo), // The first collector
  maxBy(Integer::compareTo), // The second collector
  (min, max) -> // Receives the result from those collectors and combines them
));

猜你喜欢

转载自blog.csdn.net/zhouzhiwengang/article/details/112332759
今日推荐