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
));