摘要:寫這個文章其實主要是因為剛有個童鞋問了個問題正寫的帶勁安利的實現方式,結果還沒寫完無意發現問題被關閉了哎都寫了一半了又不想放棄,就干脆寫成文章問題主要就是把集合里的數據按照一定大小順序平均分成若干組的問題,看起來挺簡單的,不過我開始看到就想
寫這個文章其實主要是因為剛有個童鞋問了個問題https://segmentfault.com/q/10...
正寫的帶勁安利Java8的實現方式,結果還沒寫完...無意發現問題被關閉了...哎...都寫了一半了...又不想放棄,就干脆寫成文章
問題主要就是把集合里的數據按照一定大小順序平均分成若干組的問題,看起來挺簡單的,不過我開始看到就想用用stream來實現,但是想了想Collectors里并沒有適合的方法,所以就想到了用定制的collector來實現了。
原問題的截圖:
正式開始回答(我是直接把之前的回答copy過來的哈):
集合處理的話,我還是推薦Java8的stream,題主這個問題設計到分組,那自然就要涉及到stream的collect方法了,這個方法是收集數據的意思,該方法的參數就是一個Collector接口,只要傳入一個Collector的實現類就可以了,常用的實現比如在工具類Collectors里有toList,toMap等,已經幫你默認寫了收集為集合或者Map的實現類了,但是明顯這些實現類都不合適,所以這里需要定制一個Collector接口的實現啦
其實就是仿照Collectors里的內部類CollectorImpl寫一個就是了...
=====================(Collector介紹,如果你已經清楚可以略過的...)==================
介紹哈Collector
Supplier supplier() BiConsumer accumulator() BinaryOperator combiner() Function finisher() Setcharacteristics()
方法中有泛型,所以要先要介紹哈Collector中的三個泛型T, A, R
T:stream在調用collect方法收集前的數據類型
A:A是T的累加器,遍歷T的時候,會把T按照一定的方式添加到A中,換句話說就是把一些T通過一種方式變成A
R:R可以看成是A的累加器,是最終的結果,是把A匯聚之后的數據類型,換句話說就是把一些A通過一種方式變成R
了解了泛型的意思,咱們結合Collectors.toList構造的默認實現類的實現方式來看看Collector接口的方法
public staticCollector > toList() { return new CollectorImpl<>((Supplier >) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID); }
官方寫的很簡單,很隨意...
前三個參數分別對應了Collector的前三個方法,也就是
(Supplier>) ArrayList::new
對應Supplier supplier()第一個方法
List::add 對應BiConsumer accumulator()第二個方法
(left, right) -> { left.addAll(right); return left; }對應BinaryOperator combiner()第三個方法
所以對應著來看就清楚了
Supplier supplier() 怎么創建一個累加器(這里對應的是如何創建一個List)
BiConsumer accumulator()怎么把一個對象添加到累加器中(這里對應的是如何在List里添加一個對象,當然是調用add方法咯)
BinaryOperator combiner()怎么把一個累加器和另一個累加器合并起來(這里對應的是如何把List和List合并起來,當然是調用addAll,這里由于最終要返回List,所以A和R是一個類型,都是List所以才調用addAll)
再來看看第四個方法Function finisher(),其實就是怎么把A轉化為R,由于是toList,所以A和R是一樣的類型,這里其實用就是Function.identity
最后第五個方法Set
=====================(Collector介紹完了)==================
現在創建自定義的collector,類名我就叫NumberCollectorImpl,由于collector這里要求有三個泛型,根據題主的需求,這三個泛型只有第一個是未知的,另外兩個應該是確認的List結構,所以寫出來應該是這么個效果
static class NumberCollectorImplimplements Collector >, List >>
ok,針對collector要求實現的5個方法來依次說明
第一個方法Supplier>> supplier()
,很明顯應該就是ArrayList::new
第二個方法BiConsumer>, T>
,這個稍微麻煩點,起始應該寫成(list, item) -> {},主要就是補充{}中的代碼了
最開始的遍歷的時候,這個list其實是父list,它肯定是空的,所以這個時候要創建一個新子List,然后把item塞進子list中,最后再把創建的新子list放入到父list中
if (list.isEmpty()){ list.add(this.createNewList(item)); }
這里簡單封了一個小方法createNewList,因為待會還要用
private ListcreateNewList(T item){ List newOne = new ArrayList (); newOne.add(item); return newOne; }
若父list不為空,那就要把當前父list中最后一個子list取出來,若空的話,當然又是要創建一個新子list然后按照之前的方法做,若不為空,就判斷子list大小咯,若大小超過2,就再次創建一個新子list然后塞item,若沒有超過就在之前子list中塞入item,寫出來大概就是這個樣子
Listlast = (List ) list.get(list.size() - 1); if (last.size() < 2){ last.add(item); }else{ list.add(this.createNewList(item)); }
第三個方法BinaryOperator>> combiner()
,其實就是兩個List如何合并,當然是addAll方法
(list1, list2) -> { list1.addAll(list2); return list1; };
第四個方法Function>, List
,由于這個時候A和R的類型一樣,都是List>> finisher()
>
,所以這里直接就是Function.identity()啦
最后一個方法Set
綜上所述,完整代碼如下
/** * 自定義Collector * * @author imango * @since 2017/7/13 */ public class CustomCollectors { // 默認采用2個一起分組 public staticCollector >, List >> groupByNumber(){ return CustomCollectors.groupByNumber(2); } // 根據number的大小進行分組 public static
Collector >, List >> groupByNumber(int number){ return new NumberCollectorImpl(number); } /** * 個數分組器 * @param
*/ static class NumberCollectorImpl implements Collector >, List >> { // 每組的個數 private int number; public NumberCollectorImpl(int number) { this.number = number; } @Override public Supplier
>> supplier() { return ArrayList::new; } @Override public BiConsumer
>, T> accumulator() { return (list, item) -> { if (list.isEmpty()){ list.add(this.createNewList(item)); }else { List
last = (List ) list.get(list.size() - 1); if (last.size() < number){ last.add(item); }else{ list.add(this.createNewList(item)); } } }; } @Override public BinaryOperator >> combiner() { return (list1, list2) -> { list1.addAll(list2); return list1; }; } @Override public Function
>, List
>> finisher() { return Function.identity(); } @Override public Set
characteristics() { return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)); } private List createNewList(T item){ List newOne = new ArrayList (); newOne.add(item); return newOne; } } }
外面那個類CustomCollectors 主要是為了封裝NumberCollectorImpl類,以后也可以把其他自定義的收集器實現放在這里面,并對外提供工具方法,并且我在NumberCollectorImpl類中新增了一個number成員變量,這樣就可以自定義分組大小了,CustomCollectors提供了兩個對外方法groupByNumber,帶參數的那個就是可以自定義分組個數的了,沒有參數的就是默認按照2個分組了,這樣的話,測試寫法就是這樣
public static void main(String[] args) { Listlist = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 按照2個分組 List > twoNumberList = list.stream().collect(CustomCollectors.groupByNumber()); // 按照5個分組 List
> fiveNumberList = list.stream().collect(CustomCollectors.groupByNumber(5)); }
這樣代碼就非常漂亮了~哈哈哈~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/71143.html
摘要:歸約操作計算有效訂單總金額有效訂單總金額收集數據收集將流轉換為其他形式,方法作為終端操作,接收一個接口的實現,用于給中元素做匯總的方法。 接上一篇:《Java8新特性之stream》,下面繼續接著講Stream 5、流的中間操作 常見的流的中間操作,歸為以下三大類:篩選和切片流操作、元素映射操作、元素排序操作:showImg(https://segmentfault.com/img/b...
摘要:一創建里流的四種方式第一種通過得方法串行流或者方法并行流創建。終止操作時一次性全部處理,稱為延遲加載篩選切片過濾中建操作。終止操作只有執行終止操作才會執行全部。即延遲加載結果中建操作。截斷流,使其元素不超過給定數量。返回流中最大值。 Stream api **Stream api 是java8 中提供的對集合處理的api , 對數據進行一系列的中間操作,元數據不會發生改變 ...
摘要:表達式體現了函數式編程的思想,即一個函數亦可以作為另一個函數參數和返回值,使用了函數作參數返回值的函數被稱為高階函數。對流對象進行及早求值,返回值不在是一個對象。 Java8主要的改變是為集合框架增加了流的概念,提高了集合的抽象層次。相比于舊有框架直接操作數據的內部處理方式,流+高階函數的外部處理方式對數據封裝更好。同時流的概念使得對并發編程支持更強。 在語法上Java8提供了Lamb...
摘要:一收集器接口陳楊收集器接口匯聚操作的元素類型即流中元素類型匯聚操作的可變累積類型匯聚操作的結果類型接口一種可變匯聚操作將輸入元素累積到可變結果容器中在處理完所有輸入元素后可以選擇將累積的結果轉換為最終表示可選操作歸約操作 一、Stream收集器 Collector接口 package com.java.design.java8.Stream; import com.java.desi...
摘要:大家好,我是樂字節的小樂。需要注意的是很多流操作本身就會返回一個流,所以多個操作可以直接連接起來,如下圖這樣,操作可以進行鏈式調用,并且并行流還可以實現數據流并行處理操作。為集合創建并行流。 大家好,我是樂字節的小樂。說起流,我們會聯想到手機、電腦組裝流水線,物流倉庫商品包裝流水線等等,如果把手機 ,電腦,包裹看做最終結果的話,那么加工商品前的各種零部件就可以看做數據源,而中間一系列的...
閱讀 2079·2021-09-22 15:54
閱讀 1838·2021-09-04 16:40
閱讀 864·2019-08-30 15:56
閱讀 2630·2019-08-30 15:44
閱讀 2156·2019-08-30 13:52
閱讀 1129·2019-08-29 16:35
閱讀 3350·2019-08-29 16:31
閱讀 2570·2019-08-29 13:48