使用`Collector`作为终端操作

使用Collector作为终端操作

使用Collector收集stream流元素

最常使用的收集stream流元素到Listcollect(Collectors.toList())

The Collector API is handled differently in the Stream interface and in the specialized streams of numbers: IntStream, LongStream, and DoubleStream. The Stream interface has two overloads of the collect() method, whereas the streams of numbers have only one. The missing one is precisely the one that takes a collector object as an argument. So you cannot use a collector object with a specialized stream of numbers.

收集到集合

Collectors有3个方法收集stream流中的元素到Collection接口

  1. toList()

  2. toSet()

  3. toCollection()

有2个特殊方法收集到不可变的ListSet

  1. toUnmodifiableList()

  2. toUnmodifiableSet()

示例1

List<Integer> numbers =
IntStream.range(0, 10)
         .boxed()
         .collect(Collectors.toList());
System.out.println("numbers = " + numbers);

结果

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

示例2

Set<Integer> evenNumbers =
IntStream.range(0, 10)
         .map(number -> number / 2)
         .boxed()
        .collect(Collectors.toSet());
System.out.println("evenNumbers = " + evenNumbers);

结果

evenNumbers = [0, 1, 2, 3, 4]

示例3

LinkedList<Integer> linkedList =
IntStream.range(0, 10)
         .boxed()
         .collect(Collectors.toCollection(LinkedList::new));
System.out.println("linked listS = " + linkedList);

结果

linked list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

使用Collector计数

Collectors.counting()等于count()

This is worth noting, and you may be wondering why such a feature has been implemented twice with two different patterns. This question is answered in the next section about collecting in maps where you will be combining collectors to create more collectors.

Collection<String> strings = List.of("one", "two", "three");

long count = strings.stream().count();
long countWithACollector = strings.stream().collect(Collectors.counting());

System.out.println("count = " + count);
System.out.println("countWithACollector = " + countWithACollector);

结果

count = 3
countWithACollector = 3

收集到单个字符串

Collectors.joining()收集字符串stream流拼接到单个字符串。

示例1

String joined = 
    IntStream.range(0, 10)
             .boxed()
             .map(Object::toString)
             .collect(Collectors.joining());

System.out.println("joined = " + joined);

结果

joined = 0123456789

示例2

String joined = 
    IntStream.range(0, 10)
             .boxed()
             .map(Object::toString)
             .collect(Collectors.joining(", "));

System.out.println("joined = " + joined);

结果

joined = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

示例3

String joined = 
    IntStream.range(0, 10)
             .boxed()
             .map(Object::toString)
             .collect(Collectors.joining(", ", "{", "}"));

System.out.println("joined = " + joined);

结果

joined = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

注意当只有少量的字符串元素需要拼接时,可以使用String.join()或者StringJoiner,避免创建stream流的开销。

使用Predicate分区元素(收集到Map

Collector API提供了3中模式从stream流中的元素创建Map。第一种模式是使用partitionningBy()创建布尔键Map<Boolean, List<T>>

示例

Collection<String> strings =
    List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Boolean, List<String>> map =
    strings.stream()
           .collect(Collectors.partitioningBy(s -> s.length() > 4));

map.forEach((key, value) -> System.out.println(key + " :: " + value));

结果

false :: [one, two, four, five, six, nine, ten]
true :: [three, seven, eight, eleven, twelve]

Collectors.partitioningBy()等重载方法下面介绍。

使用groupingBy()收集到Map

The second collector we present is very important because it allows you to create histograms.

stream流中的元素分组到Map

A key is computed for each element of the stream by applying an instance of Function to it. This function is provided as an argument of the groupingBy() method. It is called a classifier in the Collector API.

There is no restriction on this function apart from the fact that it should not return null.

示例

Collection<String> strings =
    List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Integer, List<String>> map =
    strings.stream()
           .collect(Collectors.groupingBy(String::length));

map.forEach((key, value) -> System.out.println(key + " :: " + value));

结果

3 :: [one, two, six, ten]
4 :: [four, five, nine]
5 :: [three, seven, eight]
6 :: [eleven, twelve]

groupingBy()收集的Map中的Listvalues进行后置处理

计算MapList个数

The groupingBy() method also accepts another argument, which is another collector. This collector is called a downstream collector in the Collector API, but it is just a regular collector. What makes it a downstream collector is the fact that it is passed as an argument to the creation of another collector.

This downstream collector is used to collect the values of the map created by the groupingBy() collector.

示例

Collection<String> strings =
        List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
                "ten", "eleven", "twelve");

Map<Integer, Long> map =
    strings.stream()
           .collect(
               Collectors.groupingBy(
                   String::length, 
                   Collectors.counting()));

map.forEach((key, value) -> System.out.println(key + " :: " + value));

结果

3 :: 4
4 :: 3
5 :: 3
6 :: 2
拼接Map中的List字符串

示例

Collection<String> strings =
        List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
                "ten", "eleven", "twelve");

Map<Integer, String> map =
        strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                Collectors.joining(", ")));
map.forEach((key, value) -> System.out.println(key + " :: " + value));

结果

3 :: one, two, six, ten
4 :: four, five, nine
5 :: three, seven, eight
6 :: eleven, twelve

控制实例Map

The last overload of this groupingBy() method takes an instance of a Supplier as an argument to give you control on which instance of Map you need this collector to create.

使用toMap()收集到Map

这也是第二种模式创建Map

Collectors.toMap()接收2个Function参数

  1. 第一个是创建key的映射

  2. 第二个是创建value的映射

This collector is not used in the same cases as the Collectors.groupingBy(). In particular, it does not handle the case where several elements of your stream generate the same key. In that case, by default, an IllegalStateException is raised.

example

List<User> users = ...;

Map<Long, User> userCache = 
    users.stream()
         .collect(User::getPrimaryKey, 
                 Function.idendity());

当key出现冲突,可以调用重载toMap(),添加BinaryOperator参数处理冲突

example

Collection<String> strings =
    List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Integer, String> map =
    strings.stream()
            .collect(
                    Collectors.toMap(
                            element -> element.length(),
                            element -> element, 
                            (element1, element2) -> element1 + ", " + element2));

map.forEach((key, value) -> System.out.println(key + " :: " + value));

In this example, the three arguments passed to the toMap() method are the following:

  1. element -> element.length() is the key mapper.
  2. element -> element is the value mapper.
  3. (element1, element2) -> element1 + ", " + element2) is the merge function, called with the two elements that have generated the same key.

result

3 :: one, two, six, ten
4 :: four, five, nine
5 :: three, seven, eight
6 :: eleven, twelve

As for the groupingBy() collector, you can pass a supplier as an argument to the toMap() method to control what instance of the Map interface this collector will use.

The toMap() collector has a twin method, toConcurrentMap() that will collect your data in a concurrent map. The exact type of the map is not guaranteed by the implementation.

从直方图中提取最大值

The groupingBy() collector is your best pattern to compute histograms on the data you need to analyze. Let us examine a complete example where you build a histogram and then try to find the maximum value in it based a certain criterion.

提取一个明确的最大值

example

Collection<String> strings =
    List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Integer, Long> histogram =
    strings.stream()
            .collect(
                    Collectors.groupingBy(
                            String::length,
                            Collectors.counting()));

histogram.forEach((key, value) -> System.out.println(key + " :: " + value));

result

3 :: 4
4 :: 3
5 :: 3
6 :: 2

当你想获取直方图的最大值3 :: 4

example

Map.Entry<Integer, Long> maxValue =
    histogram.entrySet().stream()
             .max(Map.Entry.comparingByValue())
             .orElseThrow();

System.out.println("maxValue = " + maxValue);

jdk6新增record,用record来代替上面Map.Entry更具有可读性

example

record NumberOfLength(int length, long number) {
    
    static NumberOfLength fromEntry(Map.Entry<Integer, Long> entry) {
        return new NumberOfLength(entry.getKey(), entry.getValue());
    }

    static Comparator<NumberOfLength> comparingByLength() {
        return Comparator.comparing(NumberOfLength::number);
    }
}
NumberOfLength maxNumberOfLength =
    histogram.entrySet().stream()
             .map(NumberOfLength::fromEntry)
             .max(NumberOfLength.comparingByLength())
             .orElseThrow();

System.out.println("maxNumberOfLength = " + maxNumberOfLength);

result

maxNumberOfLength = NumberOfLength[length=3, number=4]

提取不明确的最大值

example

Collection<String> strings =
    List.of("two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Integer, Long> histogram =
    strings.stream()
            .collect(
                    Collectors.groupingBy(
                            String::length,
                            Collectors.counting()));

histogram.forEach((key, value) -> System.out.println(key + " :: " + value));

result

3 :: 3
4 :: 3
5 :: 3
6 :: 2

如果使用之前的方法,则会提取一个返回,隐藏其他2个。

使用中间Collectors

Collector API同样提供了一些中间操作:映射、过滤和flatmapping(平面映射)。

In fact, these special collectors cannot be created alone. The factory methods that you can use to create them all need a downstream collector as a second argument.

So, the overall collector you can create with these methods is a combination of an intermediate operation and a terminal operation.

通过Collector映射

example

Collection<String> strings =
    List.of("one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

List<String> result = 
    strings.stream()
        .collect(
            Collectors.mapping(String::toUpperCase, Collectors.toList()));

System.out.println("result = " + result);

可以使用中间操作来解决上面提取不明确最大值的问题。

通过Collector过滤和flatmapping

Collectors.filtering()

Collectors.flatMapping()

使用终端Collectors

There Collector API also offers several terminal operations that correspond to terminal operations available on the Stream API.

  • maxBy(), minBy

  • summingInt(), summingLong(), summingDouble()

  • averagingInt(), averagingLong(), averagingDouble()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值