java8的stream api能很方便咱們對數據進行統計分類等工做,之前咱們寫的不少統計數據的代碼每每是循環迭代獲得的,不說別人看不懂,本身的代碼放久了也要從新看一段時間才能看得懂。如今,java8吸取了適合科學計算的語言的新特性,提供了stream api,讓咱們方便而且直觀地編寫統計代碼成爲可能。java
stream裏有一個collect(Collector c)方法,須要傳入Collector收集器這個接口。如今就說說這個接口定義的職責。sql
public interface Collector<T, A, R> { Supplier<A> supplier(); BiConsumer<A, T> accumulator(); BinaryOperator<A> combiner(); Function<A, R> finisher(); Set<Characteristics> characteristics(); }
Collector主要定義了容器的類型,添加元素的方法,容器合併的方法還有輸出的結果。api
supplier就是生成容器,accumulator是添加元素,combiner是合併容器,finisher是輸出的結果,characteristics是定義容器的三個屬性,包括是否有明確的finisher,是否須要同步,是否有序。app
例如:Collectors裏面有個tolist()方法,返回一個收集器以下ide
Collector(ArrayList::new,List::add,List::addAll,IDENTITY_FINISH)
能夠看到,先是一個ArrayList的實例化,而後是添加元素使用List的add方法,容器合併就是addAll方法。這裏沒有finisher,緣由是最終結果要的就是List,finisher就是返回容器。可是stream.collect()方法如何得知?就是經過枚舉類型獲得的。三個枚舉類型:學習
IDENTITY_FINISH 不用finisher,doc的描述爲elided(能夠省略的) UNORDERED 集合是無序的 CONCURRENT 集合的操做須要同步
定義好collector,最終傳參進stream的collect方法裏,這個終結的操做最後會經過你定義的統計和收集的操做進行收集。jdk源碼中有一個Collectors類已經爲咱們定義好不少操做,咱們只要簡單的添加一些收集的定義就能爲咱們很好的工做了。spa
這裏重點講一個groupingBy()方法。若果咱們學過sql語句的話會了解到,groupby這個方法咱們會經常用到。如今咱們經過看源碼瞭解這個方法是怎麼實現的。這個方法的最終樣貌是下面code
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)
當你使用的是blog
groupingBy(e->e.getName())
其實調用了接口
groupingBy(e->e.getName(),toList())
而最後調用了
groupingBy(e->e.getName(),HashMap::new,toList())
也就是上面長長的一坨。究竟這個groupingBy方法給咱們構造了一個怎樣的Collector呢?如今咱們分析一下。
以上面爲例子,咱們知道最後結果承載的容器是Map,更加準確的說,是一個HashMap。因此
supplier = HashMap::new
而後是accmulator,首先經過e->e.getName()得到Map的key,而後生成或取出key對應的value,而後對於toList()來講,value是List,而後將元素加進List中,能夠獲得
accmulator = (map,elemet)-> { 1. 獲得key, 2. 從map中經過key得到container,沒有container的話實例化一個新的container (經過downstream.supplier獲得List), 3. 對container執行downstream.accmulator方法,也就是add方法 }
上面的downstream就是toList方法給咱們返回的collector,咱們稱之爲下游收集器。
接着是combiner也使用了downstream.combiner,不過容器是map,獲得
mapmerger(downstream.combiner)便是addAll
這裏須要參考map的默認方法mapmerger方法。這個方法的大體意思是對map中每個key進行遍歷。
private static <K, V, M extends Map<K,V>> BinaryOperator<M> mapMerger(BinaryOperator<V> mergeFunction) { return (m1, m2) -> { for (Map.Entry<K,V> e : m2.entrySet()) m1.merge(e.getKey(), e.getValue(), mergeFunction); return m1; }; }
最後咱們就能獲得這麼一個收集器
Collector(supplier = HashMap::new, accmulator = { 獲得key,獲得container,使用下游的accmulator} combiner = mapmerger(downstream.combiner) )
finisher和characteristics不詳細說明,由於通常來講finisher能夠省略,當不能省略的時候,就是有下游收集器的finisher,源碼裏面有體會,須要理解清楚的能夠認真源碼。
再給一個例子:
collect(groupingBy(e->e.getArtist(),mapping(artist->artist.getName())))
最後獲得的收集器
Collector(supplier = HashMap::new, accmulator = { 獲得key,獲得container, 使用下游的accmulator.accept(container,artist->artist.getName()) } combiner = mapmerger(downstream.combiner) )
-------------------------------------------------------------------------------------------------------------------------------------------------------------- 這是快樂的分割線 ------------------------------------------------------------------------------------------------------------------------------------------------------------------
總結:
收集器功能強大,通常來講,jdk自帶的Collectors裏面的方法已經能知足通常需求,而瞭解Collectors的內部,讓咱們更加了解如何使用它。在寫這篇博文的時候,我對collector的印象也加深了,因此建議你們也寫一下blog,本身也會受益良多。不斷學習新特性,有機會的話把它們加入到代碼裏。
但願這篇博文能讓你有一丟丟收穫!