在 Java 12 裏面有個很是好用但在官方 JEP 沒有公佈的功能,由於它只是 Collector 中的一個小改動,它的做用是 merge 兩個 collector 的結果,這句話顯得很抽象,老規矩,咱們先來看個圖: html
管道改造常常會用這個小東西,一般咱們叫它「三通」,它的主要做用就是將 downstream1 和 downstream2 的流入合併,而後從 merger 流出java
有了這個形象的說明咱們就進入正題吧面試
上面提到的小功能就是 Collectors.teeing API, 先來看一下 JDK 關於該 API 的說明,看着以爲難受的直接忽略,繼續向下看例子就行了:編程
/** * Returns a {@code Collector} that is a composite of two downstream collectors. * Every element passed to the resulting collector is processed by both downstream * collectors, then their results are merged using the specified merge function * into the final result. * * <p>The resulting collector functions do the following: * * <ul> * <li>supplier: creates a result container that contains result containers * obtained by calling each collector's supplier * <li>accumulator: calls each collector's accumulator with its result container * and the input element * <li>combiner: calls each collector's combiner with two result containers * <li>finisher: calls each collector's finisher with its result container, * then calls the supplied merger and returns its result. * </ul> * * <p>The resulting collector is {@link Collector.Characteristics#UNORDERED} if both downstream * collectors are unordered and {@link Collector.Characteristics#CONCURRENT} if both downstream * collectors are concurrent. * * @param <T> the type of the input elements * @param <R1> the result type of the first collector * @param <R2> the result type of the second collector * @param <R> the final result type * @param downstream1 the first downstream collector * @param downstream2 the second downstream collector * @param merger the function which merges two results into the single one * @return a {@code Collector} which aggregates the results of two supplied collectors. * @since 12 */ public static <T, R1, R2, R> Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1, Collector<? super T, ?, R2> downstream2, BiFunction<? super R1, ? super R2, R> merger) { return teeing0(downstream1, downstream2, merger); }
API 描述重的一句話很是關鍵:併發
Every element passed to the resulting collector is processed by both downstream collectors 結合「三通圖」來講明就是,集合中每個要被傳入 merger 的元素都會通過 downstream1 和 downstream2 的加工處理app
其中 merger 類型是 BiFunction,也就是說接收兩個參數,並輸出一個值,請看它的 apply 方法框架
@FunctionalInterface public interface BiFunction<T, U, R> { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); }
至於能夠如何處理,咱們來看一些例子吧maven
爲了更好的說明 teeing 的使用,列舉了四個例子,看過這四個例子再回看上面的 API 說明,相信你會柳暗花明了ide
先來看一個經典的問題,給定的數字集合,須要映射整數流中的元素數量和它們的和工具
class CountSum { private final Long count; private final Integer sum; public CountSum(Long count, Integer sum) { this.count = count; this.sum = sum; } @Override public String toString() { return "CountSum{" + "count=" + count + ", sum=" + sum + '}'; } }
經過 Collectors.teeing 處理
CountSum countsum = Stream.of(2, 11, 1, 5, 7, 8, 12) .collect(Collectors.teeing( counting(), summingInt(e -> e), CountSum::new)); System.out.println(countsum.toString());
運行結果:
CountSum{count=7, sum=46}
咱們經過 teeing 一次性獲得咱們想要的結果,繼續向下看其餘例子:
經過給定的集合, 一次性計算出集合的最大值與最小值,一樣新建一個類 MinMax,並建立構造器用於 merger 收集結果
class MinMax { private final Integer min; private final Integer max; public MinMax(Integer min, Integer max) { this.min = min; this.max = max; } @Override public String toString() { return "MinMax{" + "min=" + min + ", max=" + max + '}'; } }
經過 teeing API 計算結果:
MinMax minmax = Stream.of(2, 11, 1, 5, 7, 8, 12) .collect(Collectors.teeing( minBy(Comparator.naturalOrder()), maxBy(Comparator.naturalOrder()), (Optional<Integer> a, Optional<Integer> b) -> new MinMax(a.orElse(Integer.MIN_VALUE), b.orElse(Integer.MAX_VALUE)))); System.out.println(minmax.toString());
運行結果:
MinMax{min=1, max=12}
爲了驗證一下 Optional,咱們將集合中添加一個 null 元素,並修改一下排序規則來看一下排序結果:
MinMax minmax = Stream.of(null, 2, 11, 1, 5, 7, 8, 12) .collect(Collectors.teeing( minBy(Comparator.nullsFirst(Comparator.naturalOrder())), maxBy(Comparator.nullsLast(Comparator.naturalOrder())), (Optional<Integer> a, Optional<Integer> b) -> new MinMax(a.orElse(Integer.MIN_VALUE), b.orElse(Integer.MAX_VALUE))));
運行結果:
MinMax{min=-2147483648, max=2147483647}
接下來舉一個更貼合實際的操做對象的例子
// 定義瓜的類型和重量 class Melon { private final String type; private final int weight; public Melon(String type, int weight) { this.type = type; this.weight = weight; } public String getType() { return type; } public int getWeight() { return weight; } } // 總重和單個重量列表 class WeightsAndTotal { private final int totalWeight; private final List<Integer> weights; public WeightsAndTotal(int totalWeight, List<Integer> weights) { this.totalWeight = totalWeight; this.weights = weights; } @Override public String toString() { return "WeightsAndTotal{" + "totalWeight=" + totalWeight + ", weights=" + weights + '}'; } }
經過 teeing API 計算總重量和單個列表重量
List<Melon> melons = Arrays.asList(new Melon("Crenshaw", 1200), new Melon("Gac", 3000), new Melon("Hemi", 2600), new Melon("Hemi", 1600), new Melon("Gac", 1200), new Melon("Apollo", 2600), new Melon("Horned", 1700), new Melon("Gac", 3000), new Melon("Hemi", 2600) ); WeightsAndTotal weightsAndTotal = melons.stream() .collect(Collectors.teeing( summingInt(Melon::getWeight), mapping(m -> m.getWeight(), toList()), WeightsAndTotal::new)); System.out.println(weightsAndTotal.toString());
運行結果:
WeightsAndTotal{totalWeight=19500, weights=[1200, 3000, 2600, 1600, 1200, 2600, 1700, 3000, 2600]}
繼續一個更貼合實際的例子吧:
class Guest { private String name; private boolean participating; private Integer participantsNumber; public Guest(String name, boolean participating, Integer participantsNumber) { this.name = name; this.participating = participating; this.participantsNumber = participantsNumber; } public boolean isParticipating() { return participating; } public Integer getParticipantsNumber() { return participantsNumber; } public String getName() { return name; } } class EventParticipation { private List<String> guestNameList; private Integer totalNumberOfParticipants; public EventParticipation(List<String> guestNameList, Integer totalNumberOfParticipants) { this.guestNameList = guestNameList; this.totalNumberOfParticipants = totalNumberOfParticipants; } @Override public String toString() { return "EventParticipation { " + "guests = " + guestNameList + ", total number of participants = " + totalNumberOfParticipants + " }"; } }
經過 teeing API 處理
var result = Stream.of( new Guest("Marco", true, 3), new Guest("David", false, 2), new Guest("Roger",true, 6)) .collect(Collectors.teeing( Collectors.filtering(Guest::isParticipating, Collectors.mapping(Guest::getName, Collectors.toList())), Collectors.summingInt(Guest::getParticipantsNumber), EventParticipation::new )); System.out.println(result);
其中咱們定義了 var result 來收集結果,並無指定類型,這個語法糖也加速了咱們編程的效率
運行結果:
EventParticipation { guests = [Marco, Roger], total number of participants = 11 }
其實 teeing API 就是靈活應用 Collectors 裏面定義的靜態方法,將集合元素經過 downstream1 和 downstream2 進行處理,最終經過 merger 收集起來,當項目中有同時獲取兩個收集結果時,是時候應用咱們的 teeing API 了
歡迎持續關注公衆號:「日拱一兵」
- 前沿 Java 技術乾貨分享
- 高效工具彙總 | 回覆「工具」
- 面試問題分析與解答
- 技術資料領取 | 回覆「資料」
以讀偵探小說思惟輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......
原文出處:https://www.cnblogs.com/FraserYu/p/11811242.html