Collector(重要)
- collect:收集器
- Collector:收集器的方法
- Collector:是一個接口,一種可變的匯聚操作,它將輸入元素累積到可變結果容器中。在處理完所有輸入元素後,可以選擇將累積的結果轉換爲最終形式(這是一種可選操作),支持串行,並行操作
- Collectors本身提供了關於Collector的常見匯聚實現,Collectors本身實際上是一個工廠(輔助類)
- 爲了確保串行與並行操作結果的的等價性,Collector函數需要滿足兩個條件:identity(同一性)與associativity(結合性)
- identity(同一性指中間容器類型)約束表示,對於任何部分累積的結果,將其與空的結果容器組合必鬚生成等效的結果。也就是說,對於任何一系列累加器和組合器調用的部分累加結果a 這時的a必須滿足 a=combiner.apply(a, supplier.get()),【也就是說a數據類型跟一個空結果數據數據類型相同時 兩者執行的合併結果等於本身a】
/*
combiner.apply 調用的是BinaryOperator函數 輸入兩個相同參數返回 相同值
a, supplier.get() 就相當於 list1,list2 執行a.addAll(supplier.get()); 當, supplier.get()(返回的結果容器)爲空時
a 就等於combiner.apply(a, supplier.get())
*/
(List<String> list1, List<String> list2) -> list1.addAll(list2);
- finisher(結合性指返回結果類型)關聯性約束表示,串行和並行計算必須產生等效的結果。也就是說,對於任何輸入元素t1和t2,下面計算中的結果r1和r2必須相等:
A a1 = supplier.get();//得到結果容器
accumulator.accept(a1, t1); //a1執行累加中間值,t1 是流中要處理的下一個元素
accumulator.accept(a1, t2);//此時a1已經加過t1
R r1 = finisher.apply(a1); // 串行操作
A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
R r2 = finisher.apply(combiner.apply(a2, a3));//各自並行操作 結果彙總
函數式編程的的最大特點:表示做什麼,而不是如何做
重要方法
/**
創建並返回新的可變結果容器的函數。
A:中間可變容器儲存類型
*/
Supplier<A> supplier();
/**
將值(流元素)摺疊(放到類似集合add)到可變結果容器中的函數,將新數據元素合併到結果容器中
將T 初類型放到 新A類型
(如果是串行流不會掉combiner方法)
*/
BiConsumer<A, T> accumulator();
/** 並行(多線程)
接受兩個部分結果並將其合併的函數。組合器函數可以將狀態從一個參數摺疊(放到類似集合add)到另一個參數並返回該參數將,
或者返回一個新的結果容器(例如生成新的集合)。
A:中間可變容器儲存類型
如果並行流去操作收集器此時未設置Characteristics.CONCURRENT特性時 此時會被調用(需收集多線程結果)返回行爲(返回行爲纔算被調用)
如果設置了改特性 依然不會被調用 (此時多線程操作一箇中間容器,結果都在一箇中間容器中)
*/
BinaryOperator<A> combiner();
/**
執行從中間累積類型A到最終結果類型R的最終轉換。
返回匯聚結果類型R
如果設置了特徵IDENTITY_TRANSFORM,
則該函數可以被假定爲具有從A到R的未經檢查的轉換的IDENTITY TRANSFORM
*/
Function<A, R> finisher();
/*
設置收集器的特性 內置枚舉類型
*/
Set<Characteristics> characteristics();
/*
返回由給定的供應商(Supplier)、累加器(BiConsumer)和組合器函數(BinaryOperator)描述的新收集器。結 果收集器具有集熱器特性標識飾面特點。
*/
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(accumulator);
Objects.requireNonNull(combiner);
Objects.requireNonNull(characteristics);
Set<Characteristics> cs = (characteristics.length == 0)
? Collectors.CH_ID
: Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
characteristics));
return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);
}
/*
Collectors.collectingAndThen 中間轉換
將Collector<T,A,R> 收集器的到數據在進行Function<R,RR>轉換,返回轉換後的值
*/
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher) {
Set<Collector.Characteristics> characteristics = downstream.characteristics();
if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {
if (characteristics.size() == 1)
characteristics = Collectors.CH_NOID;
else {
characteristics = EnumSet.copyOf(characteristics);
characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),
characteristics);
}
enum Characteristics {
/**
指示此收集器是併發的,這意味着結果容器可以支持與來自多個線程的同一中間結果容器同時調用的累加器函數。
如果併發收集器也不是無序的,則僅當應用於無序數據源時,才應併發計算該收集器。
(並行,多個線程操作唯一一箇中間結果容器)
*/
CONCURRENT,
/**
* 指示集合操作不承諾保留輸入元素的相遇順序。
* (如果結果容器沒有內在順序(如集合),則可能是這樣。(不排序)
*/
UNORDERED,
/**
表示finisher函數就是identity函數,可以省略。
如果設置了該枚舉參數 就是告訴間容器類型和返回類型一致
底層源碼從A到R的未檢查強制轉換是否合理,也就是說設置這個參數必須中間容器類型和返回類型
必須一致
*/
IDENTITY_FINISH
}
例:of方法如何創建
/*
Widget 元素類型,?:中間類型,TreeSet<Widget> 返回結果類型
TreeSet::new 新生成的類型容器
TreeSet::add 每個元素累積 最終容器
(left, right) -> { left.addAll(right); return left; }
將累加的每個中間集合合併一起 合併完返回
*/
Collector<Widget, ?, TreeSet<Widget>> intoSet =
Collector.of(TreeSet::new, TreeSet::add,
(left, right) -> { left.addAll(right); return left; });
等於 toCollection(Supplier)
等價
R container = collector.supplier().get();//執行TreeSet::new 行爲
* for (T t : data)
* collector.accumulator().accept(container, t);//執行TreeSet::add行爲
* return collector.finisher().apply(container);//執行(left, right) -> { left.addAll(right); return left; } 行爲
Collectors
實現各種有用的匯聚操作的收集器Collector的實現,如將元素累加到集合中、根據各種條件彙總元素等。
public class Student {
private String username;
private int score;
public Student(String username, int score) {
this.username = username;
this.score = score;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", score=" + score +
'}';
}
}
示例
public class StreamTest1 {
public static void main(String[] args) {
Student student1 = new Student("zhangsan", 80);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 100);
Student student4 = new Student("zhaoliu", 90);
Student student5 = new Student("zhaoliu", 90);
List<Student> list = Arrays.asList(student1, student2, student3, student4, student5);
// List<Student> collect = list.stream().collect(toList());
// collect.forEach(System.out::println);
//
// System.out.println("count=" + list.stream().collect(counting()));
// System.out.println("count=" + list.stream().count());
//
// list.stream().min(Comparator.comparingInt(Student::getScore)).ifPresent(x -> System.out.println(x.getScore()));
System.out.println("==========");
list.stream().collect(Collectors.minBy(Comparator.comparingInt(Student::getScore))).ifPresent(x -> System.out.println(x.getScore()));
list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Student::getScore))).ifPresent(x -> System.out.println(x.getScore()));
System.out.println(list.stream().collect(Collectors.averagingDouble(x -> x.getScore())));
System.out.println(list.stream().collect(Collectors.summingInt(x -> x.getScore())));
System.out.println(list.stream().collect(Collectors.summarizingInt(x -> x.getScore())));
System.out.println("=====");
System.out.println(list.stream().map(Student::getUsername).collect(Collectors.joining()));
System.out.println(list.stream().map(Student::getUsername).collect(Collectors.joining(",")));
System.out.println(list.stream().map(Student::getUsername).collect(Collectors.joining(",", "開始", "結束")));
System.out.println("=====");
/*
先根據分數進行分組,分組完的的數據在通過名字進行分組
*/
Map<Integer, Map<String, List<Student>>> collect = list.stream()
.collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getUsername)));
System.out.println(collect);
System.out.println("=====");
//分區
Map<Boolean, List<Student>> collect1 = list.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 80));
System.out.println(collect1);
Map<Boolean, Map<Boolean, List<Student>>> collect2 = list.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 80, Collectors.partitioningBy(y -> y.getScore() > 90)));
System.out.println(collect2);
System.out.println("=========");
/*
統計分數》80的學生 通過分數進行分組 取出來每個分組總個數
*/
Map<Integer, Long> collect3 = list.stream().filter(x -> x.getScore() > 80).collect(Collectors.groupingBy(Student::getScore, Collectors.counting()));
System.out.println(collect3);//{100=1, 90=3}
System.out.println("==========");
/*
根據名字進行分組,在去得到學生最小分數
minBy 返回的數據是Optional 類型 流中數值可能爲空
Collectors.collectingAndThen 中間轉換 將收集器的到數據在進行轉換,返回轉換後的值
*/
list.stream().
collect(Collectors.groupingBy(Student::getUsername, Collectors.minBy(Comparator.comparingInt(Student::getScore))));
Map<String, Student> collect4 = list.stream().
collect(Collectors.groupingBy(Student::getUsername,
Collectors.collectingAndThen(Collectors.minBy(Comparator.comparingInt(Student::getScore)), Optional::get)));
System.out.println(collect4);
}
}
Collectors 方法解析
例如toList,其他方法大同小異
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}