日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

累积:轻松自定义Java收集器

發布時間:2023/12/3 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 累积:轻松自定义Java收集器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Accumulative是針對Collector<T, A, R>的中間累積類型A提出的接口Collector<T, A, R>以使定義自定義Java Collector更加容易。

介紹

如果您曾經使用過Java Stream ,那么很可能使用了一些Collector ,例如:

  • Collectors.toList
  • Collectors.toMap

但是你有沒有使用過……

  • 組成的 Collector ?
    • 它使用另一個 Collector作為參數,例如: Collectors.collectingAndThen 。
  • 定制 Collector ?
    • 其功能在Collector.of明確指定。
  • 這篇文章是關于custom Collector的。

    集電極

    讓我們回想一下Collector合同的本質 (我的評論):

    /** * @param <T> (input) element type * @param <A> (intermediate) mutable accumulation type (container) * @param <R> (output) result type */ public interface Collector<T, A, R> { Supplier<A> supplier(); // create a container BiConsumer<A, T> accumulator(); // add to the container BinaryOperator<A> combiner(); // combine two containers Function<A, R> finisher(); // get the final result from the container Set<Characteristics> characteristics(); // irrelevant here }

    上面的合同本質上是功能性的,這非常好! 這使我們可以使用任意累積類型( A )創建Collector ,例如:

    • A : StringBuilder ( Collectors.joining )
    • A : OptionalBox ( Collectors.reducing )
    • A : long[] ( Collectors.averagingLong )

    提案

    在我提供任何理由之前,我將提出建議,因為它很簡短。 該提議的完整源代碼可以在GitHub上找到 。

    累積接口

    我建議將以下稱為Accumulative (名稱待討論)的接口添加到JDK:

    public interface Accumulative<T, A extends Accumulative<T, A, R>, R> { void accumulate(T t); // target for Collector.accumulator() A combine(A other); // target for Collector.combiner() R finish(); // target for Collector.finisher() }

    與Collector相反,此接口本質上是面向對象的 ,實現該接口的類必須表示某種可變狀態

    過載收集器

    具有Accumulative ,我們可以添加以下Collector.of重載:

    public static <T, A extends Accumulative<T, A, R>, R> Collector<T, ?, R> of( Supplier<A> supplier, Collector.Characteristics... characteristics) { return Collector.of(supplier, A::accumulate, A::combine, A::finish, characteristics); }

    普通開發者故事

    在本部分中,我將展示該建議會對普通開發人員產生怎樣的影響,而一般開發人員僅了解 Collector API的基礎知識 。 如果您精通此API,請在繼續閱讀之前盡力想象您不知道。

    讓我們重用我最近的文章中的示例(進一步簡化)。 假設我們有一個Stream :

    interface IssueWiseText { int issueLength(); int textLength(); }

    并且我們需要計算問題覆蓋率 :

    總發行時長
    ─────────────
    總文字長度

    此要求轉換為以下簽名:

    Collector<IssueWiseText, ?, Double> toIssueCoverage();

    一般的開發人員可能會決定使用自定義累積類型A來解決此問題(不過其他解決方案也是可能的 )。 假設開發人員將其命名為CoverageContainer這樣:

    • T : IssueWiseText
    • A : CoverageContainer
    • R : Double

    在下面,我將展示這樣的開發人員如何實現CoverageContainer的結構

    無累積結構

    注意 :本節很長,目的是說明該過程對于沒有使用Collector的開發人員可能有多復雜 。 如果您已經意識到這一點則可以跳過它

    如果沒有Accumulative ,則開發人員將查看Collector.of ,并看到四個主要參數:

  • Supplier<A> supplier
  • BiConsumer<A, T> accumulator
  • BinaryOperator<A> combiner
  • Function<A, R> finisher
  • 要處理Supplier <A> supplier ,開發人員應:

  • 在Supplier<A>中用心理替代A獲得Supplier<CoverageContainer>
  • 在精神上將簽名解析為CoverageContainer get ()
  • 回想一下JavaDoc for Collector.supplier()
  • 第四種調用方法的引用 ( 對構造函數的引用 )
  • 意識到supplier = CoverageContainer::new
  • 要處理BiConsumer <A, T> accumulator ,開發人員應:

  • BiConsumer<CoverageContainer, IssueWiseText>
  • void accept (CoverageContainer a, IssueWiseText t)
  • 在精神上將簽名轉換為一種實例方法
    void accumulate(IssueWiseText t)
  • 第三種調用方法的引用 ( 引用特定類型的任意對象的實例方法 )
  • 意識到accumulator = CoverageContainer::accumulate
  • 處理BinaryOperator <A> combiner :

  • BinaryOperator<CoverageContainer>
  • CoverageContainer apply (CoverageContainer a, CoverageContainer b)
  • CoverageContainer combine(CoverageContainer other)
  • combiner = CoverageContainer::combine
  • 要處理Function <A, R> finisher :

  • Function<CoverageContainer, Double>
  • Double apply (CoverageContainer a)
  • double issueCoverage()
  • finisher = CoverageContainer::issueCoverage
  • 這個漫長的過程導致:

    class CoverageContainer { void accumulate(IssueWiseText t) { } CoverageContainer combine(CoverageContainer other) { } double issueCoverage() { } }

    開發人員可以定義toIssueCoverage() (必須以正確的順序提供參數):

    Collector<IssueWiseText, ?, Double> toIssueCoverage() { return Collector.of( CoverageContainer:: new , CoverageContainer::accumulate, CoverageContainer::combine, CoverageContainer::finish ); }

    累積結構

    現在, 使用 Accumulative ,開發人員將查看新的Collector.of重載,并且將僅看到一個主要參數:

  • Supplier<A> supplier
  • 和一個有界類型參數 :

    • A extends Accumulative<T, A, R>

    因此,開發人員將自然而然地開始- 實施 Accumulative<T, A, R>并第一次和最后一次解析T , A和R :

    class CoverageContainer implements Accumulative<IssueWiseText, CoverageContainer, Double> { }

    此時,一個不錯的IDE會抱怨該類必須實現所有抽象方法。 而且,這是最美麗的部分 ,它將提供快速修復。 在IntelliJ中,您單擊“ Alt + Enter”→“實施方法”,然后…就完成了!

    class CoverageContainer implements Accumulative<IssueWiseText, CoverageContainer, Double> { @Override public void accumulate(IssueWiseText issueWiseText) { ????} @Override public CoverageContainer combine(CoverageContainer other) { return null ; } @Override public Double finish() { return null ; } }

    因此,您不必擺弄類型,手動編寫任何內容或命名任何內容!

    哦,是的-您仍然需要定義toIssueCoverage() ,但是現在很簡單:

    Collector<IssueWiseText, ?, Double> toIssueCoverage() { return Collector.of(CoverageContainer:: new ); }

    那不是很好嗎?

    實作

    這里的實現無關緊要,因為這兩種情況( diff )幾乎相同。

    基本原理

    程序太復雜

    我希望我已經演示了如何定義自定義Collector是一個挑戰。 我必須說,即使我總是不愿意定義一個。 但是,我也感覺到-有了Accumulative ,這種勉強就會消失,因為該過程將縮小為兩個步驟:

  • 實現Accumulative<T, A, R>
  • 調用Collector.of(YourContainer::new)
  • 推動實施

    JetBrains創造了“ 發展動力 ”,我想將其轉變為“實施動力”。

    由于Collector是一個簡單的功能的設備中,這通常是沒有意義的(據我可以告訴)來實現它(也有例外 )。 但是,通過Google搜索“實施收集器”可以看到(約5000個結果)人們正在這樣做。

    這很自然,因為要在Java中創建“自定義” TYPE ,通常會擴展/實現TYPE 。 實際上,即使是經驗豐富的開發人員(例如Java冠軍Tomasz Nurkiewicz )也可以做到這一點。

    總結起來,人們感到有實現的動力 ,但在這種情況下,JDK沒有為他們提供實現的任何東西。 Accumulative可以填補這一空白……

    相關例子

    最后,我搜索了一些示例,這些示例可以輕松實現Accumulative 。

    在OpenJDK(盡管這不是目標位置)中,我發現了兩個:

  • Collectors.reducing ( diff )
  • Collectors.teeing ( diff )
  • 對堆棧溢出,雖然,我發現大量的: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 , 52 , 53 。

    我還發現了一些基于數組的示例,可以將其重構為Accumulative以獲得更好的可讀性: a , b , c 。

    命名

    Accumulative不是最好的名字,主要是因為它是一個形容詞 。 但是,我選擇它是因為:

    • 我希望名稱以A開頭(如<T, A, R> ),
    • 我最好的候選人( Accumulator )已經被BiConsumer<A, T> accumulator() ,
    • AccumulativeContainer似乎太長。

    在OpenJDK中, A稱為:

    • 可變結果容器
    • 累積類型
    • 容器

    提示以下替代方法:

    • AccumulatingBox
    • AccumulationState
    • Collector.Container
    • MutableResultContainer

    當然,如果這個想法被接受,這個名字將通過“傳統”的名字

    摘要

    在本文中,我建議向JDK添加Accumulative接口和新的Collector.of重載。 有了它們,開發人員將不再費勁地創建自定義Collector 。 取而代之的是,它只是成為“執行合同”和“引用構造函數”。

    換句話說,該提案旨在降低進入“定制Collector世界的門檻

    附錄

    下面的可選閱讀。

    解決方案示例:JDK 12+

    在JDK 12+中,由于Collectors.teeing ( JDK-8209685 ),我們將toIssueCoverage()定義為組合的Collector 。

    static Collector<IssueWiseText, ?, Double> toIssueCoverage() {return Collectors.teeing(Collectors.summingInt(IssueWiseText::issueLength),Collectors.summingInt(IssueWiseText::textLength),(totalIssueLength, totalTextLength) -> (double) totalIssueLength / totalTextLength); }

    上面的內容很簡潔,但是對于Collector API新手來說,可能很難遵循。

    示例解決方案:JDK方法

    另外, toIssueCoverage()可以定義為:

    static Collector<IssueWiseText, ?, Double> toIssueCoverage() {return Collector.of(() -> new int[2],(a, t) -> { a[0] += t.issueLength(); a[1] += t.textLength(); },(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },a -> (double) a[0] / a[1]); }

    我稱其為“ JDK方式”,因為某些Collector的實現與OpenJDK中的實現類似(例如Collector.averagingInt )。

    但是,盡管這樣的簡潔代碼可能適用于OpenJDK,但由于可讀性高(這很低,我稱之為cryptic ),因此它肯定不適合業務邏輯。

    翻譯自: https://www.javacodegeeks.com/2019/02/accumulative-custom-java-collectors.html

    總結

    以上是生活随笔為你收集整理的累积:轻松自定义Java收集器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。