Collector工作原理

https://blog.csdn.net/xiliunian/article/details/88773718

導(dǎo)航

引例

Collector

什么是Collector

Collector工作原理

特征值

自定義Collector

Collectors詳解

求值

均值:averaging

元素個(gè)數(shù):counting

最值:maxBy嫉晶、minBy

和:summing皂岔、summarizing

分組

groupingBy

groupingByConcurrent

partitioningBy(分區(qū))

其他操作

collectingAndThen

joining

mapping

reducing

toCollection、toList與toSet

toMap與toConcurrentMap

引例
在Java8新特性(二) Stream(流)一文中,由于篇幅有限冷守,有關(guān)聚合操作collect使用到的Collector沒(méi)有展開(kāi)分析格郁,本文將會(huì)詳細(xì)講解Collector以及Collectors缠劝。

老規(guī)矩先來(lái)看一個(gè)例子妖泄。到了收獲的季節(jié),農(nóng)場(chǎng)主需要把果園里的蘋(píng)果根據(jù)顏色分類(lèi)客税,然后送往銷(xiāo)售商那邊冀膝。只是將蘋(píng)果分類(lèi)是難不倒我們的,下面用代碼實(shí)現(xiàn)農(nóng)場(chǎng)主的需求:

public class Apple{

private String color;
private Integer weight;

public Apple(String color, Integer weight) {
    this.color = color;
    this.weight = weight;
}

public String getColor() {
    return color;
}

public Integer getWeight() {
    return weight;
}

@Override
public String toString() {
    return "Apple [color=" + color + ", weight=" + weight + "]";
}

}
public class TestCollectByNormal {

public static List<Apple> orchard = Arrays.asList(new Apple("green", 150),
        new Apple("red", 170), new Apple("green", 100), new Apple("red", 170), 
        new Apple("yellow", 170), new Apple("green", 150));

public static void main(String[] args) {
    Map<String, List<Apple>> baskets = new HashMap<>();
    for (Apple apple : orchard) {
        String color = apple.getColor();
        List<Apple> basket = baskets.get(color);
        if (null == basket) {
            basket = new ArrayList<>();
            baskets.put(color, basket);
        }
        basket.add(apple);
    }
    System.out.println(baskets);
}

}
既然我們?cè)趯W(xué)習(xí)Java8新特性霎挟,不妨用學(xué)到的東西來(lái)重寫(xiě)上面的例子:

public class TestCollectByOptional {

public static void main(String[] args) {
    Map<String, List<Apple>> baskets = new HashMap<>();
    TestCollectByNormal.orchard.forEach(apple -> {
        List<Apple> basket = Optional.ofNullable(baskets.get(apple.getColor()))
                .orElseGet(() -> {
                    List<Apple> tempbasket = new ArrayList<>();
                    baskets.put(apple.getColor(), tempbasket);
                    return tempbasket;
                });
        basket.add(apple);
    });
    System.out.println(baskets);
}

}
比較上面的兩種做法窝剖,二者的代碼量都不少,也不夠簡(jiǎn)潔酥夭。下面使用Collector來(lái)改進(jìn):

public class TestCollectByCollector {

public static void main(String[] args) {
    Map<String, List<Apple>> baskets = TestCollectByNormal.orchard.stream()
            .collect(Collectors.groupingBy(Apple::getColor));
    System.out.println(baskets);
}

}
借助于Collector赐纱,我們只用一行代碼就完成了上面兩種方法中的所有操作。這就是Collector的強(qiáng)大之處熬北。

Collector
什么是Collector
JavaDoc中對(duì)Collector的描述如下:

A mutable reduction operation that accumulates input elements into a mutable result container, optionally transforming the accumulated result into a final representation after all input elements have been processed. Reduction operations can be performed either sequentially or in parallel.

Collector是一種可變的匯聚操作疙描,它將輸入元素累積到一個(gè)可變的結(jié)果容器中。在所有的元素處理完成后讶隐,Collector將累積的結(jié)果轉(zhuǎn)換成一個(gè)最終的表示(這是一個(gè)可選的操作)起胰。Collector支持串行和并行兩種方式執(zhí)行。

Collector接口中聲明五個(gè)方法和一個(gè)枚舉常量:

public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
enum Characteristics {
CONCURRENT,
UNORDERED,
IDENTITY_FINISH
}
....
}

Collector接口有三個(gè)泛型巫延,它們的含義如下:

T:輸入的元素類(lèi)型
A:累積結(jié)果的容器類(lèi)型
R:最終生成的結(jié)果類(lèi)型
Collector工作原理
Collector通過(guò)下面四個(gè)方法協(xié)同工作以完成匯聚操作:

supplier: 創(chuàng)建新的結(jié)果容器
accumulator:將輸入元素合并到結(jié)果容器中
combiner:合并兩個(gè)結(jié)果容器(并行流使用效五,將多個(gè)線程產(chǎn)生的結(jié)果容器合并)
finisher:將結(jié)果容器轉(zhuǎn)換成最終的表示
下面是串行流情況下Collector的工作邏輯:

首先supplier會(huì)提供結(jié)果容器地消,然后accumulator向結(jié)果容器中累積元素,最后finisher將結(jié)果容器轉(zhuǎn)換成最終的返回結(jié)果畏妖。如果結(jié)果容器類(lèi)型和最終返回結(jié)果類(lèi)型一致脉执,那么finisher就可以不執(zhí)行,這就是之前說(shuō)這是一個(gè)可選的操作的原因戒劫。

而combiner是和并行流相關(guān)的半夷,在串行流中combiner并不起作用。JavaDoc中介紹如下:

A function that accepts two partial results and merges them. The combiner function may fold state from one argument into the other and return that, or may return a new result container.

combiner方法接受兩個(gè)部分的結(jié)果并合并他們迅细,該方法可能會(huì)把一個(gè)結(jié)果容器折疊到另一個(gè)結(jié)果容器中并返回巫橄,也可能返回一個(gè)新的結(jié)果容器。

假如在并行流中對(duì)元素的操作分別在三條線程中完成茵典,三條線程會(huì)返回三個(gè)結(jié)果容器湘换。此時(shí)combiner就可能會(huì)這樣處理多個(gè)線程中的結(jié)果容器:先將線程2的結(jié)果容器2中元素合并到線程1的結(jié)果容器1中,并返回結(jié)果容器1敬尺;再把線程3的結(jié)果容器3中的元素合并到線程1的結(jié)果容器1中;最后返回結(jié)果容器1贴浙。

特征值
除上述四個(gè)方法Collector中還有一個(gè)characteristics()方法砂吞,該方法用于給Collector實(shí)現(xiàn)類(lèi)設(shè)置特征值。枚舉常量Characteristics 中共有三個(gè)特征值崎溃,它們的具體含義如下:

CONCURRENT:表示結(jié)果容器只有一個(gè)(即使是在并行流的情況下)蜻直。只有在并行流且收集器不具備此特性的情況下,combiner()返回的lambda表達(dá)式才會(huì)執(zhí)行(中間結(jié)果容器只有一個(gè)就無(wú)需合并)袁串。設(shè)置此特性時(shí)意味著多個(gè)線程可以對(duì)同一個(gè)結(jié)果容器調(diào)用概而,因此結(jié)果容器必須是線程安全的。
UNORDERED:表示流中的元素?zé)o序囱修。
IDENTITY_FINISH:表示中間結(jié)果容器類(lèi)型與最終結(jié)果類(lèi)型一致赎瑰。設(shè)置此特性時(shí)finiser()方法不會(huì)被調(diào)用。

自定義Collector
明白Collector的原理之后破镰,我們就可以自定義Collector實(shí)現(xiàn)餐曼。下面我們完成一個(gè)Collector實(shí)現(xiàn)——將輸入元素收集到Set集合中。

明確需求之后鲜漩,我們就可以確定下來(lái)Collector實(shí)現(xiàn)的三個(gè)泛型具體是什么:

T(輸入的元素類(lèi)型):T
A(累積結(jié)果的容器類(lèi)型):Set<T>
R(最終生成的結(jié)果類(lèi)型):Set<T>
完成的Collector實(shí)現(xiàn)如下:

public class MySetCollector<T> implements Collector<T, Set<T>, Set<T>> {

@Override
public Supplier<Set<T>> supplier() {
    System.out.println("supplier invoked");
    return HashSet::new;
}

@Override
public BiConsumer<Set<T>, T> accumulator() {
    System.out.println("accumulator invoked");

// return HashSet<T>::add; // 報(bào)錯(cuò)
/**
* 作為accumulator而言源譬,它能明確的僅僅是supplier提供的結(jié)果容器類(lèi)型是Set類(lèi)型,
* 而不知道supplier提供的具體結(jié)果容器類(lèi)型(這里是HashSet)孕似。
* 如果supplier提供的結(jié)果容器類(lèi)型是TreeSet類(lèi)型踩娘,
* 那么accumulator使用HashSet提供的add方法就會(huì)出錯(cuò)。
* 因此這里應(yīng)該使用Set提供的add方法喉祭。
*/
return Set<T>::add;

}

@Override
public BinaryOperator<Set<T>> combiner() {
    System.out.println("combiner invoked");
    return (set1, set2) -> {
        set1.addAll(set2);
        return set1;
    };
}

@Override
public Function<Set<T>, Set<T>> finisher() {
    System.out.println("finisher invoked");
    return Function.identity();   // return t -> t; 
}

@Override
public Set<Characteristics> characteristics() {
    System.out.println("characteristics invoked");
    // 結(jié)果容器類(lèi)型和最終結(jié)果類(lèi)型一致养渴,設(shè)置IDENTITY_FINISH特性
    return Collections.unmodifiableSet(EnumSet
            .of(Collector.Characteristics.IDENTITY_FINISH));
}

}
測(cè)試我們自定義的Collector實(shí)現(xiàn)是否發(fā)揮了作用:

public class TestMyCollector {

public static void main(String[] args) {
    List<String> data = Arrays.asList("hello", "world", "hello");
    Set<String> result = data.stream().collect(new MySetCollector<>());
    System.out.println(result);
}

}
打印結(jié)果如下:

supplier invoked
accumulator invoked
combiner invoked
characteristics invoked
characteristics invoked
[world, hello]

打印結(jié)果是符合我們的預(yù)期的雷绢,但是從打印結(jié)果中,我們可以發(fā)現(xiàn)兩個(gè)問(wèn)題:

1厚脉、“finisher invoked”并沒(méi)有打印习寸,說(shuō)明finisher()方法沒(méi)有被調(diào)用。

查看collect()方法實(shí)現(xiàn)(位于ReferencePipeline類(lèi))傻工,我們來(lái)看該方法是如何調(diào)用Collector的:

public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if (isParallel() // 這一段是并行流邏輯霞溪,可以跳過(guò)不看
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
}
else {
container = evaluate(ReduceOps.makeRef(collector));
}
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}

我們?cè)谧远x的Collector實(shí)現(xiàn)——MySetCollector中設(shè)置了IDENTITY_FINISH特性,通過(guò)上面的源碼可以知道:如果Collector實(shí)現(xiàn)中沒(méi)有IDENTITY_FINISH特性中捆,才會(huì)調(diào)用實(shí)現(xiàn)類(lèi)中的finisher()方法鸯匹,否則直接將中間結(jié)果容器強(qiáng)轉(zhuǎn)成最終的結(jié)果類(lèi)型。

因此只有在百分百確定中間結(jié)果類(lèi)型和最終的結(jié)果類(lèi)型一致時(shí)泄伪,才可以為實(shí)現(xiàn)類(lèi)設(shè)置IDENTITY_FINISH特性殴蓬,否則很可能會(huì)出現(xiàn)類(lèi)型轉(zhuǎn)換異常。

2蟋滴、"combiner invoked"打印了染厅,然而我們的程序中使用的是串行流。

繼續(xù)跟進(jìn)到makeRef()方法中:

public static <T, I> TerminalOp<T, I> // makeRef部分源碼
makeRef(Collector<? super T, I, ?> collector) {
Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
BiConsumer<I, ? super T> accumulator = collector.accumulator();
BinaryOperator<I> combiner = collector.combiner();
....
}

從上面的源碼中可以看到津函,makeRef()方法中用一個(gè)變量記錄調(diào)用combiner()方法返回的Lambda表達(dá)式肖粮,所以"combiner invoked"會(huì)被打印,而該Lambda表達(dá)式并沒(méi)有被調(diào)用(可以在此Lambda表達(dá)式中加入打印語(yǔ)句驗(yàn)證)尔苦。

Collectors詳解
Collectors是一個(gè)工具類(lèi)涩馆,提供常用的Collector實(shí)現(xiàn)。Collectors中定義有實(shí)現(xiàn)Collector接口的內(nèi)部類(lèi)CollectorImpl,Collectors提供方法的返回值都是CollectorImpl對(duì)象。

public final class Collectors {
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {...}
...
}

求值
均值:averaging
averaging操作可以計(jì)算輸入元素的均值诡右,該操作包括三個(gè)方法:

<T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper)
<T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper)
<T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper)
public class TestCollectors1 {

public static List<Apple> data = Arrays.asList(new Apple("green", 210), 
        new Apple("red", 170), new Apple("green", 100), new Apple("red", 170), 
        new Apple("yellow", 170), new Apple("green", 150));
 
public static void main(String[] args) {
    Double result = data.stream()
            .collect(Collectors.averagingInt(Apple::getWeight));
    System.out.println(result);
}

}
打印結(jié)果如下:

161.66666666666666

元素個(gè)數(shù):counting
counting()方法可以統(tǒng)計(jì)輸入元素的個(gè)數(shù)。

<T> Collector<T, ?, Long> counting()
public class TestCollectors2 {

public static void main(String[] args) {
    Long result = TestCollectors1.data.stream().collect(Collectors.counting());
    System.out.println(result);
    Long emptyStreamResult = Stream.empty().collect(Collectors.counting());   
    System.out.println(emptyStreamResult);      // 空的流中沒(méi)有元素,返回0
}

}
打印結(jié)果如下:

6
0

值得思考的是涯雅,Stream也提供有相同功能的count()方法:

long count = TestCollectors1.appleList.stream().count(); // 該方法返回流中元素個(gè)數(shù)

為什么在兩個(gè)地方提供擁有相同功能的方法呢?雖然這兩種操作都可以達(dá)到相同的目的展运,但是這兩個(gè)方法的返回值類(lèi)型是不同的——count()方法的返回值是long型斩芭,counting()方法返回值是Collector類(lèi)型。在后面談到分組操作時(shí)乐疆,我們可以看到counting()方法的返回值又可以作為其他方法的參數(shù)划乖,實(shí)現(xiàn)更高效的操作。

所以對(duì)于同一個(gè)目的挤土,可能有多個(gè)方法可以實(shí)現(xiàn)琴庵,我們需要根據(jù)情況選擇最優(yōu)的那個(gè)。

最值:maxBy、minBy
maxBy和minBy操作可以找出輸入元素中的最大最小值迷殿。

<T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
<T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
public class TestCollectors3 {

public static void main(String[] args) {
    TestCollectors1.data.stream()
        .collect(Collectors.maxBy(Comparator.comparingInt(Apple::getWeight)))
        .ifPresent(System.out::println);
}

}
打印結(jié)果如下:

Apple [color=green, weight=210]

和:summing儿礼、summarizing
summing操作可以計(jì)算輸入元素的總和,該操作包含三個(gè)方法:

<T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper)
<T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper)
<T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper)
相較于summing操作只能計(jì)算和庆寺,summarizing操作則可以提供更強(qiáng)大的計(jì)算:該操作可以統(tǒng)計(jì)輸入元素個(gè)數(shù)蚊夫、和以及最值。summarizing操作也提供三個(gè)方法:

<T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)
<T> Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)
<T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)
public class TestCollectors4 {

public static void main(String[] args) {
    testSummingInt();
    testSummarizingInt();
}

private static void testSummingInt() {
    Integer result = TestCollectors1.data.stream()
            .collect(Collectors.summingInt(Apple::getWeight));
    System.out.println(result);
}

private static void testSummarizingInt() {
    System.out.println("===================");
    IntSummaryStatistics result = TestCollectors1.data.stream()
            .collect(Collectors.summarizingInt(Apple::getWeight));
    System.out.println(result);
}

}
打印結(jié)果如下:

970

IntSummaryStatistics{count=6, sum=970, min=100, average=161.666667, max=210}

分組
groupingBy
groupingBy操作可以將輸入元素進(jìn)行分組懦尝。Collectors提供三個(gè)重載的groupingBy()方法:

groupingBy(Function<? super T, ? extends K> classifier):classifier提供結(jié)果Map(HashMap)的鍵
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream):downstream提供結(jié)果Map和值
groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream): mapFactory指定結(jié)果Map的類(lèi)型
前面介紹counting()方法的時(shí)候說(shuō)過(guò) :counting()方法的返回值又可以作為其他方法的參數(shù)知纷。擁有兩個(gè)及以上參數(shù)的groupingBy()方法,可以接收一個(gè)Collector類(lèi)型參數(shù)作為Map的值陵霉。這時(shí)counting()方法就可以派上用場(chǎng):

public class TestCollectors5 {

public static void main(String[] args) {
    TreeMap<String, Long> result = TestCollectors1.data.stream()
            .collect(Collectors.groupingBy(Apple::getColor, 
                    TreeMap::new, Collectors.counting()));
    System.out.println(result);
}

}
打印結(jié)果如下:

{green=3, red=2, yellow=1}

groupingByConcurrent
具體操作同groupingBy()琅轧,將元素整理成ConcurrentMap。

public class TestCollectors6 {

public static void main(String[] args) {
    ConcurrentSkipListMap<String, Double> result = TestCollectors1.data
            .stream().collect(Collectors.groupingByConcurrent(Apple::getColor, 
            ConcurrentSkipListMap::new, Collectors.averagingInt(Apple::getWeight)));
    System.out.println(result);
}

}
打印結(jié)果如下:

{green=153.33333333333334, red=170.0, yellow=170.0}

partitioningBy(分區(qū))
分區(qū)是分組的一種特殊情況踊挠,該操作將輸入元素分為兩類(lèi)(即鍵是true和false的Map)乍桂。Collectors提供兩個(gè)重載的partitioningBy()方法:

partitioningBy(Predicate<? super T> predicate):predicate提供分區(qū)依據(jù)
partitioningBy(Predicate<? super T> predicate,Collector<? super T, A, D> downstream):downstream提供結(jié)果Map的值
public class TestCollectors7 {

public static void main(String[] args) {
    Map<Boolean, Double> collect = TestCollectors1.data.stream()
            .collect(Collectors.partitioningBy(
                    apple -> "green".equals(apple.getColor()), 
                    Collectors.averagingInt(Apple::getWeight)));
        System.out.println(collect);
}

}
打印結(jié)果如下:

{false=170.0, true=153.33333333333334}

其他操作
collectingAndThen
collectingAndThen()方法接收兩個(gè)參數(shù):downstream(Collector類(lèi)型)和finisher(Function類(lèi)型),在調(diào)用downstream之后效床,將調(diào)用結(jié)果值作為finisher的傳入值睹酌,再調(diào)用finisher。

collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)
public class TestCollectors8 {

public static void main(String[] args) {
    Optional.ofNullable(TestCollectors1.data.stream().collect(Collectors
            .collectingAndThen(Collectors.averagingInt(Apple::getWeight), 
                    item ->  "average weight is " + item)))
            .ifPresent(System.out::println);
}

}
打印結(jié)果如下:

average weight is 161.66666666666666

joining
joining操作可以將輸入元素(字符串類(lèi)型)拼接成字符串剩檀。Collectors提三個(gè)重載的joining()方法:

joining():拼接輸入元素
joining(CharSequence delimiter):將delimiter作為分隔符
joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix):將prefix作為前綴憋沿,suffix作為后綴
public class TestCollectors9 {

public static void main(String[] args) {
    String result = TestCollectors1.data.stream()
            .map(Apple::getColor).collect(Collectors.joining(",", "Color[", "]"));
    System.out.println(result);     
}

}
打印結(jié)果如下:

Color[green,red,green,red,yellow,green]

mapping
mapping()方法接收兩個(gè)參數(shù):mapper(Function類(lèi)型)和downstream(Collector類(lèi)型),在調(diào)用mapper之后谨朝,將調(diào)用結(jié)果的返回值作為downstream的輸入元素卤妒,再調(diào)用downstream甥绿。

不難發(fā)現(xiàn)此方法調(diào)用參數(shù)的順序和collectingAndThen()方法相反字币。

mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream)
public class TestCollectors10 {

public static void main(String[] args) {
    String result = TestCollectors1.data.stream()
            .collect(Collectors.mapping(Apple::getColor, Collectors.joining()));
    System.out.println(result);
}

}
打印結(jié)果如下:

greenredgreenredyellowgreen

reducing
reducing操作可以對(duì)輸入元素執(zhí)行匯聚操作。Collectors提供三個(gè)重載的reducing()方法:

reducing(BinaryOperator<T> op):對(duì)輸入的元素應(yīng)用op操作
reducing(T identity, BinaryOperator<T> op):提供初始值identity
reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op):在對(duì)元素進(jìn)行op操作之前共缕,先進(jìn)行mapper操作
public class TestCollectors11 {

public static void main(String[] args) {
    testReducingByOperator();
    testReducingByIdentityAnaOperator();
    testReducingByIdentityAnaOperatorAndFunction();
}

private static void testReducingByOperator() {
    TestCollectors1.data.stream().collect(Collectors
        .reducing(BinaryOperator.maxBy(Comparator.comparingInt(Apple::getWeight))))
            .ifPresent(System.out::println);
}

public static void testReducingByIdentityAnaOperator() {
    System.out.println("===================");
    Integer collect = TestCollectors1.data.stream().map(Apple::getWeight)
            .collect(Collectors.reducing(0, Integer::sum));
    System.out.println(collect);
}

private static void testReducingByIdentityAnaOperatorAndFunction() {
    System.out.println("===================");
    Integer collect = TestCollectors1.data.stream()
            .collect(Collectors.reducing(0, Apple::getWeight, Integer::sum));
    System.out.println(collect);
}

}
打印結(jié)果如下:

Apple [color=green, weight=210]

970

970

Stream中也提供匯聚操作reduce洗出,reduce操作強(qiáng)調(diào)是不可變性,即輸入什么類(lèi)型元素图谷,輸出還是什么類(lèi)型翩活。而Collectors提供的reducing()方法,則強(qiáng)調(diào)的是可變性便贵,輸出的類(lèi)型可以和輸入不同菠镇。

toCollection、toList與toSet
toCollection承璃、toList和toSet可以將輸入元素整理成集合:

toCollection(Supplier<C> collectionFactory):將輸入元素整理成集合利耍,collectionFactory可以指定結(jié)果集合的類(lèi)型
toList():將輸入元素整理成ArrayList
toSet():將輸入元素整理成HashSet
public class TestCollectors12 {

public static void main(String[] args) {
    testToCollection();
    testToList();
    testToSet();
}

private static void testToCollection() {
    LinkedList<Apple> result = TestCollectors1.data.stream()
        .filter(item -> item.getWeight() > 150)
            .collect(Collectors.toCollection(LinkedList::new));
    System.out.println(result.getClass() + ":" + result);
}

private static void testToList() {
    System.out.println("==============");
    List<Apple> result = TestCollectors1.data.stream()
        .filter(a -> a.getWeight() > 150).collect(Collectors.toList());
    System.out.println(result.getClass() + ":" + result);
}

private static void testToSet() {
    System.out.println("==============");
    Set<String> result = TestCollectors1.data.stream()
        .collect(Collectors.mapping(Apple::getColor, Collectors.toSet()));
    System.out.println(result.getClass() + ":" + result);
}

}
打印結(jié)果如下:

class java.util.LinkedList:[Apple [color=green, weight=210], Apple [color=red, weight=170], Apple [color=red, weight=170], Apple [color=yellow, weight=170]]

class java.util.ArrayList:[Apple [color=green, weight=210], Apple [color=red, weight=170], Apple [color=red, weight=170], Apple [color=yellow, weight=170]]

class java.util.HashSet:[red, green, yellow]

toMap與toConcurrentMap
toMap操作可以將輸入元素整理成Map,Collectors提供三個(gè)重載的toMap()方法:

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper):keyMapper和valueMapper分別提供結(jié)果Map的鍵和值
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction):mergeFunction對(duì)鍵相同的值進(jìn)行累積
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier): mapSupplier可以指定結(jié)果的Map類(lèi)型
toConcurrentMap操作同toMap,這里不再贅述隘梨。

需要注意的是程癌,只有兩個(gè)參數(shù)的toMap()(toConcurrentMap)方法的keyMapper所提供的key是不可以重復(fù)的,否則會(huì)拋出IllegalStateException轴猎。

public class TestCollectors13 {

public static void main(String[] args) {
    testToMap();
    testToMapWithBinaryOperatorAndSupplier();
}

private static void testToMap() {
    // 報(bào)錯(cuò):java.lang.IllegalStateException

// Map<String, Integer> collect = TestCollectors1.appleList.stream()
// .collect(Collectors.toMap(Apple::getColor, Apple::getWeight));
List<Apple> data = Arrays.asList(new Apple("green", 210),
new Apple("red", 170), new Apple("yellow", 170));
Map<String, Integer> result = data.stream()
.collect(Collectors.toMap(Apple::getColor, Apple::getWeight));
System.out.println(result);
}

private static void testToMapWithBinaryOperatorAndSupplier() {
    System.out.println("==============");
    Hashtable<String, Integer> result = TestCollectors1.data.stream()
        .collect(Collectors.toMap(Apple::getColor, 
                v -> 1, Integer::sum, Hashtable::new));
    System.out.println(result);
}

}
打印結(jié)果如下:

{red=170, green=210, yellow=170}

{green=3, red=2, yellow=1}
————————————————
版權(quán)聲明:本文為CSDN博主「zeroxes」的原創(chuàng)文章嵌莉,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明捻脖。
原文鏈接:https://blog.csdn.net/xiliunian/article/details/88773718

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锐峭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子郎仆,更是在濱河造成了極大的恐慌只祠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扰肌,死亡現(xiàn)場(chǎng)離奇詭異抛寝,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)曙旭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)盗舰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人桂躏,你說(shuō)我怎么就攤上這事钻趋。” “怎么了剂习?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蛮位,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鳞绕,道長(zhǎng)失仁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任们何,我火速辦了婚禮萄焦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冤竹。我一直安慰自己拂封,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布鹦蠕。 她就那樣靜靜地躺著冒签,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钟病。 梳的紋絲不亂的頭發(fā)上萧恕,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天霜定,我揣著相機(jī)與錄音,去河邊找鬼廊鸥。 笑死望浩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惰说。 我是一名探鬼主播磨德,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吆视!你這毒婦竟也來(lái)了典挑?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤啦吧,失蹤者是張志新(化名)和其女友劉穎您觉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體授滓,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琳水,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了般堆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片在孝。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淮摔,靈堂內(nèi)的尸體忽然破棺而出私沮,到底是詐尸還是另有隱情,我是刑警寧澤和橙,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布仔燕,位于F島的核電站,受9級(jí)特大地震影響魔招,放射性物質(zhì)發(fā)生泄漏晰搀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一仆百、第九天 我趴在偏房一處隱蔽的房頂上張望厕隧。 院中可真熱鬧奔脐,春花似錦俄周、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至排龄,卻和暖如春波势,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工尺铣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拴曲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓凛忿,卻偏偏與公主長(zhǎng)得像澈灼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子店溢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容