成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

圖解Stream之collect:長文深度分析讓你徹底掌握流式編程

開發 前端
collect 操作是 Stream 流處理中的關鍵一步,用于將處理后的元素以指定的方式進行收集和匯總。下面我們對collect相關的操作原理及方法進行詳細地介紹,確保我們完全掌握collect的使用。

在 Java 8 中,引入了 Stream 流的概念,它是對集合數據進行操作的一種高級抽象。Stream 具有以下幾個主要特點和優勢:

  1. 聲明式編程 通過簡潔的方式表達對數據的處理邏輯,而無需關注具體的實現細節。例如,使用 filter 方法篩選出符合條件的元素,使用 map 方法對元素進行轉換。
  2. 懶加載Stream 的操作并非立即執行,而是在終端操作(如 collect、forEach 等)被調用時才真正執行。這有助于提高性能,避免不必要的計算。
  3. 鏈式操作 可以將多個操作連接在一起,形成一個連貫的處理流程,使代碼更具可讀性和可維護性。
  4. 并行處理 可以方便地實現并行計算,充分利用多核 CPU 的優勢,提高處理大規模數據的效率。

而在 Stream 流中,collect 操作是一個終端操作,用于將 Stream 中的元素收集到一個新的集合或數據結構中。Stream 提供了對數據的一系列中間操作,如 filter、map、sorted 等,這些操作只是定義了對數據的處理邏輯,但不會真正執行對數據的處理。而 collect 操作作為終端操作,觸發之前定義的中間操作的執行,并將處理后的結果進行收集。

總之,collect 操作是 Stream 流處理中的關鍵一步,用于將處理后的元素以指定的方式進行收集和匯總。下面我們對collect相關的操作原理及方法進行詳細地介紹,確保我們完全掌握collect的使用。

Collectors介紹

我們先看看Collect、Collector和Collectors的區別:

  • collect 是 Java 8 中 Stream 流的一個方法,用于對流中的元素進行收集操作。它需要傳入一個實現了 Collector 接口的收集器來指定具體的收集行為。
  • Collector 是一個接口,定義了收集流元素的規范和方法。通過實現 Collector 接口,可以自定義收集器來實現特定的元素收集邏輯。
  • Collectors 是一個工具類,它提供了許多靜態方法,用于方便地創建常見的 Collector 實現。這些預定義的收集器可以滿足大多數常見的收集需求,例如將流元素收集到列表、集合、映射等,或者進行分組、分區、規約匯總等操作。

例如,使用 Collectors.toList() 可以創建一個將流元素收集到列表的收集器,然后將其傳遞給 collect 方法,對流進行收集操作并得到一個包含所有元素的列表。

圖片圖片

概括來說:

  • collect 是 Stream 流的終止方法,使用傳入的收集器(必須是 Collector 接口的某個具體實現類)對結果執行相關操作。
  • Collector 是一個接口,collect 方法接收的收集器是 Collector 接口的具體實現類。
  • Collectors 是一個工具類,提供了很多靜態工廠方法,用于創建各種預定義的 Collector 接口的具體實現類,方便程序員使用。如果不使用 Collectors 類,自己去實現 Collector 接口也是可以的。

圖片圖片

Collectors的方法

圖片圖片

恒等處理

指的就是Stream的元素在經過Collector函數處理前后完全不變,例如toList()操作,只是最終將結果從Stream中取出放入到List對象中,并沒有對元素本身做任何的更改處理。

圖片圖片

歸約匯總

Stream流中的元素被逐個遍歷,進入到Collector處理函數中,然后會與上一個元素的處理結果進行合并處理,并得到一個新的結果,以此類推,直到遍歷完成后,輸出最終的結果。

圖片圖片

分組分區

Collectors工具類中提供了groupingBy和partitioningBy方法進行數據分區,區別在于partitioningBy僅基于條件分成兩個組。

圖片圖片

Collector的原理

要自定義收集器Collector,需要實現Collector接口中定義的五個方法,分別是:supplier()、accumulator()、combiner()、finisher()和characteristics()。

圖片圖片

這5個方法的含義說明歸納如下:

接口名稱

功能含義說明

supplier

創建新的結果容器,可以是一個容器,也可以是一個累加器實例,總之是用來存儲結果數據的

accumlator

元素進入收集器中的具體處理操作

finisher

當所有元素都處理完成后,在返回結果前的對結果的最終處理操作,當然也可以選擇不做任何處理,直接返回

combiner

各個子流的處理結果最終如何合并到一起去,比如并行流處理場景,元素會被切分為好多個分片進行并行處理,最終各個分片的數據需要合并為一個整體結果,即通過此方法來指定子結果的合并邏輯

characteristics

對此收集器處理行為的補充描述,比如此收集器是否允許并行流中處理,是否finisher方法必須要有等等,此處返回一個Set集合,里面的候選值是固定的幾個可選項。

對于characteristics返回set集合中的可選值,說明如下:

取值

含義說明

UNORDERED

無序。聲明此收集器的匯總歸約結果與Stream流元素遍歷順序無關,不受元素處理順序影響

CONCURRENT

并行。聲明此收集器可以多個線程并行處理,允許并行流中進行處理

IDENTITY_FINISH

恒等映射。聲明此收集器的finisher方法是一個恒等操作

現在,我們知道了這5個接口方法各自的含義與用途了,那么作為一個Collector收集器,這幾個接口之間是如何配合處理并將Stream數據收集為需要的輸出結果的呢?下面這張圖可以清晰的闡述這一過程:

圖片圖片

如果我們的Collector是支持在并行流中使用的,則其處理過程有所不同:

圖片圖片

下面的例子展示如何自定義一個將元素收集到LinkedList的收集器:

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class MyCollector implements Collector<String, List<String>, List<String>> {

    // supplier()方法返回一個Supplier,它創建了一個空的LinkedList實例,作為收集數據的容器。
    @Override
    public Supplier<List<String>> supplier() {
        return LinkedList::new; 
    }

    // accumulator()方法返回一個BiConsumer,用于將流中的元素添加到LinkedList中。
    @Override
    public BiConsumer<List<String>, String> accumulator() {
        return List::add; 
    }

    // combiner()方法返回一個BinaryOperator,用于合并多個LinkedList。當流被并行處理時,可能會有多個子部分的結果需要合并,這里將兩個LinkedList合并為一個。
    @Override
    public BinaryOperator<List<String>> combiner() {
        return (r1, r2) -> {
            r1.addAll(r2);
            return r1;
        };
    }

    // finisher()方法返回一個Function,在遍歷完流后,將累加器對象(在這里就是LinkedList本身)轉換為最終結果。在這個例子中,累加器對象就是最終結果,所以直接返回它。
    @Override
    public Function<List<String>, List<String>> finisher() {
        return list -> list; 
    }

    // characteristics()方法返回一個包含收集器特征的EnumSet。這里使用了IDENTITY_FINISH特征,表示finisher方法返回的是一個恒等函數,可以跳過,直接將累加器作為最終結果。
    @Override
    public EnumSet<Collector.Characteristics> characteristics() {
        return EnumSet.of(Collector.Characteristics.IDENTITY_FINISH); 
    }
}

下面我們用自定義的收集器進行處理:

List<String> input = Arrays.asList("apple", "banana", "orange");
List<String> result = input.stream().collect(new MyCollector());

如果希望收集器具有其他特性,例如支持并行處理(CONCURRENT)、不保證元素順序(UNORDERED)等,可以在characteristics()方法中添加相應的特性。例如,如果你的收集器支持并行處理且不保證元素順序,可以這樣返回特性集合:

return EnumSet.of(Collector.Characteristics.CONCURRENT, Collector.Characteristics.UNORDERED);

另外,還可以根據具體的需求自定義收集器的邏輯,例如過濾元素、執行特定的計算等。

Collectors方法深究

groupingBy分組

Collectors.groupingBy是 Java 8 中Stream API 的一個收集器,用于將流中的元素根據某個分類函數收集到Map中。

groupingBy的構造方法

  • groupingBy(Function):基本的分組,默認使用List收集,

圖片圖片

相當于groupingBy(classifier, toList())。我們用下面的代碼實現,對學生按照年齡段進行分組:

Map<Integer, List<Student>> nameListByAge = students.stream().collect(Collectors.groupingBy(Student::getAge));
  • groupingBy(Function, Collector):可指定收集器的分組

圖片圖片

這里使用Set集合收集。

// 不同年齡段的學生集合,去重
Map<Integer, Set<String>> namesByAge = students.stream().collect(Collectors.groupingBy(
        Student::getAge,
        Collectors.mapping(Student::getName, Collectors.toSet()))
);
  • groupingBy(Function, Supplier, Collector):可指定存儲容器和收集器的分組

圖片圖片

下面使用TreeMap作為容器,保證了鍵的有序性。但是分組之后的組內數據不是有序的。

// 【鍵有序】不同年齡段的學生集合,去重,年齡按照升序排列
Map<Integer, Set<String>> namesBySortedAge = students.stream().collect(Collectors.groupingBy(
    Student::getAge,
    TreeMap::new,
    Collectors.mapping(Student::getName, Collectors.toSet()))
);

如果要保證分組之后的數據有序,有下面兩種方法:

  • collectingAndThen:先分組,再使用collectingAndThen聚合操作,對組內數據進行排序。
Map<Integer, List<Student>> sortedCollect = students.stream()
            .collect(Collectors.groupingBy(
                    Student::getAge,
                    Collectors.collectingAndThen(
                            // 先收集到List
                            Collectors.toList(),
                            // 然后對每個List進行排序
                            list -> list.stream().sorted(Comparator.comparing(Student::getScore)).collect(Collectors.toList())
                    )
            ));
  • mapping:使用第二種構造方法,對組內元素收集到list,然后使用TreeSet集合進行收集。
// 按照年齡分組,組內按照分數升序
Map<Integer, TreeSet<Student>> collect = students.stream().collect(Collectors.groupingBy(
        Student::getAge,
        Collectors.mapping(student -> student, Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getScore))))
        )
);

基礎分組功能

  • 按照對象的某個字段進行分組:假設有一個學生類Student,包含course(課程)字段,可以按照課程對學生進行分組。
Map<String, List<Student>> groupByCourse = students.stream()
  .collect(Collectors.groupingBy(Student::getCourse));
  • 自定義鍵的映射:根據學生對象的多個字段或進行某種格式化操作來生成鍵。
Map<String, List<Student>> groupByCustomKey = students.stream()
  .collect(Collectors.groupingBy(student -> student.getName() + "_" + student.getAge()));
  • 自定義容器類型:如使用LinkedHashMap保證分組后鍵的有序性。
Map<String, List<Student>> groupByCourseWithLinkedHashMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, LinkedHashMap::new, Collectors.toList()));

分組統計功能

  • 計數:計算每個分組中的元素數量。
Map<String, Long> courseCountMap = students.stream()
  .collect(Collectors.groupingBy(Student::getCourse, Collectors.counting()));
  • 求和:對每個分組中的某個數值字段進行求和。
Map<String, Integer> totalScoreByCourseMap = students.stream()
  .collect(Collectors.groupingBy(Student::getCourse, Collectors.summingInt(Student::getScore)));
  • 平均值:計算每個分組中某個數值字段的平均值。
Map<String, Double> averageScoreByCourseMap = students.stream()
  .collect(Collectors.groupingBy(Student::getCourse, Collectors.averagingInt(Student::getScore)));
  • 最大最小值:獲取每個分組中某個數值字段的最大值或最小值。
Map<String, Student> maxScoreStudentByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.maxBy(Comparator.comparingInt(Student::getScore))));

Map<String, Student> minScoreStudentByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.minBy(Comparator.comparingInt(Student::getScore))));
  • 完整統計:同時獲取計數、總和、平均值、最大最小值等統計結果。
Map<String, IntSummaryStatistics> summaryStatisticsByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.summarizingInt(Student::getScore)));
  • 范圍統計:根據某個條件進行范圍分組統計。
Map<Boolean, List<Student>> dividedByScore = students.stream()
 .collect(Collectors.partitioningBy(student -> student.getScore() >= 60));

分組合并功能

合并分組結果:使用reducing方法對每個分組的元素進行自定義的合并操作。

Map<String, String> combinedNamesByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.reducing("", Student::getName, (name1, name2) -> name1 + ", " + name2)));

合并字符串:將每個分組中的字符串元素連接起來。

Map<String, String> joinedNamesByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.joining(", ")));

分組自定義映射功能

映射結果為Collection對象:將每個分組的元素映射為另一個Collection對象。

Map<String, Set<Student>> studentsSetByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.toSet()));

自定義映射結果:通過mapping方法進行更復雜的映射操作。

Map<String, List<String>> studentNamesByCourseMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, Collectors.mapping(Student::getName, Collectors.toList())));

自定義downstream收集器:更靈活地控制分組后的值的收集方式。

Collector<Student,?, Map<String, CustomResult>> customCollector = Collector.of(
    HashMap::new, 
    (map, student) -> { 
        // 自定義的收集邏輯,將學生對象轉換為 CustomResult 并添加到 map 中 
    },
    (map1, map2) -> { 
        // 合并兩個 map 的邏輯 
    });

Map<String, CustomResult> customResultMap = students.stream()
 .collect(Collectors.groupingBy(Student::getCourse, customCollector));

多級分組可以通過嵌套使用groupingBy來實現。例如,假設有一個包含學生信息的列表,要先按班級分組,然后在每個班級內再按性別分組,可以這樣寫:

Map<String, Map<String, List<Student>>> groupedByClassAndGender = students.stream()
  .collect(Collectors.groupingBy(Student::getClass, Collectors.groupingBy(Student::getGender)));

在上述示例中,外層的groupingBy按照班級進行分組,得到的每個班級的分組結果(本身也是一個Map)又通過內層的groupingBy按照性別進一步分組。這樣最終得到的是一個兩級分組的Map結構。

partitioningBy分類

掌握了groupingBy,現在看partitioningBy就簡單很多了。就兩個簡單的構造方法:

// 僅提供分類器
partitioningBy(Predicate<? super T> predicate) 

// 提供分類器和下游收集器
partitioningBy(Predicate<? super T> predicate,Collector<? super T, A, D> downstream)

比如我們篩選成年人和非成年人:

Map<Boolean, List<Student>> adultList = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() > 18));

Map<Boolean, Set<Student>> adultSet = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() > 18, Collectors.toSet()));

結果如下圖所示:

圖片圖片

collectingAndThen分組處理

圖片圖片

從方法簽名可以看出,需要傳入一個收集器和一個處理函數,相當于收集了數據之后,再進行后續操作。如下圖所示:

圖片圖片

比如,前面提到的,先分組,再排序:

Map<Integer, List<Student>> sortedCollect = students.stream()
            .collect(Collectors.groupingBy(
                    Student::getAge,
                    Collectors.collectingAndThen(
                            // 先收集到List
                            Collectors.toList(),
                            // 然后對每個List進行排序
                            list -> list.stream().sorted(Comparator.comparing(Student::getScore)).collect(Collectors.toList())
                    )
            ));

reducing歸集操作

單參數:輸入歸集操作

  • BinaryOperator accumulator 歸集操作函數 輸入參數T返回T

圖片圖片

比如實現數組的內容求和:

List<Integer> testData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Optional<Integer> sum = testData.stream().collect(Collectors.reducing((prev, cur) -> {
    System.out.println("prev=>" + prev + "cur=>" + cur);
    return prev + cur;
}));
System.out.print(sum.get()); // 45

雙參數:輸入初始值、歸集操作 參數說明

  • T identity 返回類型T初始值
  • BinaryOperator accumulator 歸集操作函數 輸入參數T返回T

下面是增加了初始值的求和操作:

List<Integer> testData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer sum = testData.stream().collect(Collectors.reducing(20, (prev, cur) -> {
    System.out.println("prev=>" + prev + "cur=>" + cur);
    return prev + cur;
}));
System.out.print(sum); //65

三參數:這個函數才是真正體現reducing(歸集)的過程。調用者要明確知道以下三點

  1. 需要轉換類型的初始值
  2. 類型如何轉換
  3. 如何收集返回值

參數說明

  • U identity 最終返回類型U初始值
  • BiFunction<U, ? super T, U> accumulator, 將輸入參數T轉換成返回類型U的函數
  • BinaryOperator  combiner 歸集操作函數 輸入參數U返回U

圖片圖片

比如實現單數字轉字符串并按逗號連接的功能:

List<Integer> testData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
String joinStr = testData.stream().collect(Collectors.reducing("轉換成字符串", in -> {
    return in + "";
}, (perv, cur) -> {
    return perv + "," + cur;
}));
System.out.print(joinStr); // 轉換成字符串,1,2,3,4,5,6,7,8,9

責任編輯:武曉燕 來源: 松語編程
相關推薦

2021-08-11 22:17:48

負載均衡LVS機制

2024-12-02 10:15:15

2021-04-15 07:32:02

java 代碼Stream

2023-07-06 08:31:50

Python對象編程

2020-12-08 08:14:11

SQL注入數據庫

2019-09-24 08:16:14

Reactor響應式編程

2019-07-11 14:45:52

簡歷編程項目

2025-05-21 09:32:28

2024-03-15 08:23:26

異步編程函數

2009-11-06 09:39:40

WCF契約

2016-03-28 09:39:54

2019-02-25 09:20:53

2014-11-05 10:58:00

編程

2024-04-12 09:01:08

2024-06-21 09:27:05

2022-09-16 08:32:17

Reduxreact

2021-04-18 07:09:50

工具類異步編程

2025-01-20 15:06:42

2017-06-07 18:40:33

PromiseJavascript前端

2020-11-03 10:32:48

回調函數模塊
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美亚洲一区二区三区 | 天天爽夜夜操 | 成人性视频免费网站 | 怡红院成人在线视频 | 国产在线不卡 | 久草资源在线 | 久久久久高清 | 伊人青青久久 | 亚洲成人精品一区 | 一区二区av | 久久中文字幕一区 | 国产视频1区2区 | 国产高清av免费观看 | 亚洲欧美一区二区三区国产精品 | aa级毛片毛片免费观看久 | 国产午夜精品久久久 | 综合九九| 在线亚洲欧美 | 成人精品鲁一区一区二区 | 国产精品一区二区在线免费观看 | jlzzxxxx18hd护士 | 天天色综| 97国产一区二区 | 国产色 | 日本精品免费在线观看 | 夜操| 国产精品久久久久久久免费观看 | 日韩国产精品一区二区三区 | 日韩国产中文字幕 | 欧美久久久网站 | 国产成人精品一区二区三区在线观看 | 91精品国产综合久久久亚洲 | 成年人在线观看视频 | 亚洲精品九九 | 97人人澡人人爽91综合色 | 精品综合 | 夜夜骑首页 | 欧美成人高清 | 天天爽夜夜爽精品视频婷婷 | 成人欧美一区二区三区在线观看 | 久草视频网站 |