下面的簡單範例說明如何使用很難用的
Stream.collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
。
你有一個裝載多個整數的List
來源,然後要從中取出符合條件的元素放在另一個List
中。
例如下面的List<Integer>
裝有1-6的整數,我想要取出大於3的整數並放到另一個List<Integer>
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = list.stream()
.filter(e -> e > 3)
.collect(() -> new ArrayList<>(), // supplier
(c, e) -> c.add(e), // accumulator
(c1, c2) -> c1.addAll(c2)); // combiner
System.out.println(result); // [4, 5, 6]
注意在Eclipse中你可能會發現上面的c.add(e)
會報錯,請忽略錯誤直接將程式碼寫完,因為目前Eclipse對lambda語法的支援如自動完成仍有些bug。
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
接收三個參數如下
Supplier<R>
supplierBiConsumer<R,? super T>
accumulatorBiConsumer<R,R>
combiner
supplier是一個工廠方法,為無輸入參數但有回傳值的函式,你可以把supplier稱做容器供應商,負責提供裝載結果元素的新容器,supplier函式的回傳值即為新容器,也就是上面的new ArrayList<>()
。supplier的原本樣貌如下:
Supplier<ArrayList<Integer>> supplier = new Supplier<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> get() {
return new ArrayList<>();
}
};
accumulator為接收兩個參數但無回傳值的函式,
第一個參數c
(container)為裝載元素的容器,也就是supplier提供的新容器ArrayList<Integer>
。此參數的型態必須與supplier的回傳值相同。
第二個參數e
(element)為要被裝載的元素,也就是被filter()
條件所過濾後的元素,也就是filter()
過濾後的的整數。
因為collect(...)
通常是要將修改後的元素放在新容器中,所以像是List
等集合通常都是用add()
方法將元素加入。accumulator的原本樣貌如下:
BiConsumer<ArrayList<Integer>, Integer> accumulator = new BiConsumer<ArrayList<Integer>, Integer>() {
@Override
public void accept(ArrayList<Integer> c, Integer e) {
c.add(e);
}
};
combiner為接收兩個參數但無回傳值的函式,兩個參數都是supplier提供的新容器ArrayList<Integer>
。負責把所有的新容器中的元素合併。注意combiner只有在平行串流(parallel stream)才派得上用場,在平行處理時,每個元素會在accumulator函式中被放在不同的新容器中,最後透過combiner合併,因此combiner函式必須滿足結合性(Associativity)。combiner的原本樣貌如下:
BiConsumer<ArrayList<Integer>, ArrayList<Integer>> combiner = new BiConsumer<ArrayList<Integer>, ArrayList<Integer>>() {
@Override
public void accept(ArrayList<Integer> c1, ArrayList<Integer> c2) {
c1.addAll(c2);
}
};
所以lambda原本實際上長這樣:
BiConsumer<ArrayList<Integer>, ArrayList<Integer>> combiner = new BiConsumer<ArrayList<Integer>, ArrayList<Integer>>() {
@Override
public void accept(ArrayList<Integer> c1, ArrayList<Integer> c2) {
c1.addAll(c2);
}
};
Supplier<ArrayList<Integer>> supplier = new Supplier<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> get() {
return new ArrayList<>();
}
};
BiConsumer<ArrayList<Integer>, Integer> accumulator = new BiConsumer<ArrayList<Integer>, Integer>() {
@Override
public void accept(ArrayList<Integer> c, Integer e) {
c.add(e);
}
};
BiConsumer<ArrayList<Integer>, ArrayList<Integer>> combiner = new BiConsumer<ArrayList<Integer>, ArrayList<Integer>>() {
@Override
public void accept(ArrayList<Integer> c1, ArrayList<Integer> c2) {
c1.addAll(c2);
}
};
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = list.stream()
.filter(e -> e > 3)
.collect(supplier, accumulator, combiner);
System.out.println(result); // [4, 5, 6]
平行處理
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = list.stream().parallel()
.filter(e -> e > 3)
.collect(() -> new ArrayList<>(), // supplier
(c, e) -> c.add(e), // accumulator
(c1, c2) -> c1.addAll(c2)); // combiner
System.out.println(result); // [4, 5, 6]
方法參考寫法。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = list.stream().parallel()
.filter(e -> e > 3)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(result); // [4, 5, 6]
參考:
沒有留言:
張貼留言