深入理解Java中的Collectors(Stream流)

在这里插入图片描述

引言

在 Java 的 Stream API 中,Collectors 是一个非常强大的工具类,它提供了许多静态方法,用于将 Stream 的元素收集到集合、字符串或其他类型的结果中。使用 Collectors,我们可以轻松地进行数据聚合和转换操作。

文章目录

  • 引言
  • 什么是 Collectors?
  • 常用 Collectors 方法详解
    • toList()
    • toSet()
    • toMap()
    • joining()
    • groupingBy()
    • partitioningBy()
    • summarizingInt()
    • reducing()
    • counting()
  • 高级用法
    • 嵌套收集器
    • 自定义收集器
  • 注意事项

什么是 Collectors?

Collectors 类是 Java 中的一个实用工具类,包含了一系列静态方法,这些方法用于创建各种常见的集合操作。它们用于将流中的元素累积到某些结果中,例如 List、Set、Map 等,还可以进行各种统计操作,如求和、平均、最小值、最大值等。

Collectors 类位于 java.util.stream 包中,通常与 Stream 的 collect() 方法一起使用。它的设计遵循了函数式编程的原则,使得数据处理变得更加简洁和高效。

常用 Collectors 方法详解

下面将详细介绍一些常用的 Collectors 方法,并通过通俗易懂的示例来展示它们的使用。

toList()

toList() 方法是最简单的收集器,它将流中的元素收集到一个 List 中。这是一个无参数方法,返回一个新的 List。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class CollectorsToListExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry");List<String> result = fruits.stream().collect(Collectors.toList());System.out.println(result); // 输出 [apple, banana, cherry]}
}

在这个例子中,将一个包含水果名称的 List 转换为一个 Stream,并使用 Collectors.toList() 方法将其收集回一个 List。

toSet()

toSet() 方法将流中的元素收集到一个 Set 中,自动去除重复项。这也是一个无参数方法,返回一个新的 Set。

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;public class CollectorsToSetExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");Set<String> result = fruits.stream().collect(Collectors.toSet());System.out.println(result); // 输出 [banana, cherry, apple]}
}

在这个例子中,有一个包含重复元素的 List,通过 Collectors.toSet() 方法,我们将其收集到一个 Set 中,从而去除了重复元素。

toMap()

toMap() 方法需要两个函数:一个用于生成键,另一个用于生成值。它将流中的元素收集到一个 Map 中。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class CollectorsToMapExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry");Map<String, Integer> result = fruits.stream().collect(Collectors.toMap(fruit -> fruit, // 键映射函数String::length  // 值映射函数));System.out.println(result); // 输出 {banana=6, cherry=6, apple=5}}
}

在这个例子中,将水果名称作为键,水果名称的长度作为值,收集到一个 Map 中。

joining()

joining() 方法可以将流中的字符串元素连接成一个单一的字符串。它可以有三个参数:分隔符、前缀和后缀。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class CollectorsJoiningExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry");String result = fruits.stream().collect(Collectors.joining(", ", "[", "]"));System.out.println(result); // 输出 [apple, banana, cherry]}
}

在这个例子中,使用 Collectors.joining() 方法将水果名称连接成一个字符串,并指定了逗号作为分隔符,方括号作为前缀和后缀。

groupingBy()

groupingBy() 方法根据提供的函数对流中的元素进行分组,返回一个 Map,其中键是分组函数的结果,值是符合该分组的元素列表。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class CollectorsGroupingByExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apricot");Map<Character, List<String>> result = fruits.stream().collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));System.out.println(result); // 输出 {a=[apple, apricot], b=[banana], c=[cherry]}}
}

在这个例子中,按水果名称的首字母对水果进行分组。

partitioningBy()

partitioningBy() 方法根据提供的谓词对流中的元素进行分区,返回一个 Map,其中键是布尔值,值是符合或不符合谓词的元素列表。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class CollectorsPartitioningByExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apricot");Map<Boolean, List<String>> result = fruits.stream().collect(Collectors.partitioningBy(fruit -> fruit.startsWith("a")));System.out.println(result); // 输出 {false=[banana, cherry], true=[apple, apricot]}}
}

在这个例子中,根据水果名称是否以字母 ‘a’ 开头将水果分为两个部分。

summarizingInt()

summarizingInt() 方法接收一个将对象转换为 int 的函数,并返回一个 IntSummaryStatistics 对象,其中包含元素的数量、总和、最小值、平均值和最大值。

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;public class CollectorsSummarizingIntExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry");IntSummaryStatistics stats = fruits.stream().collect(Collectors.summarizingInt(String::length));System.out.println(stats); // 输出 IntSummaryStatistics{count=3, sum=17, min=5, average=5.666667, max=6}}
}

在这个例子中,统计了水果名称的长度信息。

reducing()

reducing() 方法是一个通用的归约操作,它可以将流中的元素归约成一个值。它需要三个参数:初始值、累加器和组合器。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class CollectorsReducingExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry");int totalLength = fruits.stream().collect(Collectors.reducing(0, String::length, Integer::sum));System.out.println(totalLength); // 输出 17}
}

在这个例子中,将水果名称的长度进行归约操作,计算它们的总长度。

counting()

counting() 是一个简单的归约操作,用于计算流中元素的数量。

long count = list.stream().collect(Collectors.counting());

高级用法

除了上述基本用法外,Collectors 还提供了一些高级用法,可以实现更复杂的数据处理需求。

嵌套收集器

Collectors 允许嵌套使用,这意味着我们可以在一个收集操作中组合多个收集器。例如,我们可以先按照某个条件分组,然后对每个组应用另一个收集器。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class NestedCollectorsExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");Map<Integer, Long> lengthCounts = fruits.stream().collect(Collectors.groupingBy(String::length,Collectors.counting()));System.out.println(lengthCounts); // 输出 {4=1, 5=2, 6=1, 10=1}}
}

在这个例子中,我们首先按照字符串长度分组,然后对每个组计数。

自定义收集器

虽然 Collectors 类提供了许多预定义的收集器,但有时我们可能需要创建自定义的收集器来满足特定需求。我们可以通过实现 Collector 接口来创建自定义收集器。


Collector<T, A, R> 是一个泛型接口,其中:

  • T 是要收集的元素类型
  • A 是累加器的类型(中间结果)
  • R 是最终结果的类型

这个 Collector 接口定义了五个方法:

  1. supplier(): 提供一个初始的空累加器
  2. accumulator(): 定义如何将新元素添加到累加器中
  3. combiner(): 定义如何合并两个累加器
  4. finisher(): 定义如何从累加器得到最终结果
  5. characteristics(): 定义收集器的特征

收集器的特征就像是收集器的"说明书"。它告诉Java这个收集器有什么特别之处,可以怎么用。比如,“CONCURRENT"特征就像说"多人可以同时使用”,这样Java就知道可以让多个线程一起工作。“UNORDERED"特征就像说"不用排队”,Java就知道不需要保持元素的顺序。“IDENTITY_FINISH"特征就像说"装好就直接用”,Java就知道不用再额外处理结果。有了这些"说明",Java就能更聪明地使用收集器,让程序运行得更快更好。这就像你在使用一个工具时,了解了它的特点,就能更好地发挥它的作用。

import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;public class CustomCollectorExample {public static void main(String[] args) {List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");String result = fruits.stream().collect(new CustomJoiningCollector());System.out.println(result); // 输出 "APPLE, BANANA, CHERRY, DATE"}static class CustomJoiningCollector implements Collector<String, StringBuilder, String> {@Overridepublic Supplier<StringBuilder> supplier() {return StringBuilder::new;}@Overridepublic BiConsumer<StringBuilder, String> accumulator() {return (sb, s) -> {if (sb.length() > 0) sb.append(", ");sb.append(s.toUpperCase());};}@Overridepublic BinaryOperator<StringBuilder> combiner() {return StringBuilder::append;}@Overridepublic Function<StringBuilder, String> finisher() {return StringBuilder::toString;}@Overridepublic Set<Characteristics> characteristics() {return Collections.emptySet();}}
}

这个自定义收集器将字符串转换为大写并用逗号分隔。

注意事项

  1. 在使用 Collectors 的 toMap() 方法时,如果流中存在相同的键,则默认会抛出 IllegalStateException。可以通过提供合并函数来处理键冲突。
  2. 当使用并行流时,需要注意线程安全问题。虽然 Collectors 的大多数方法在并行流中都是安全的,但在收集到特定的集合时,可能需要使用线程安全的集合或者在收集操作后进行同步。
  3. 对于大型数据集,使用 Collectors 的某些方法(如 toList、toSet)可能不如直接使用特定类型的集合(如 ArrayList、HashSet)高效。此外,groupingBy 和 partitioningBy 在处理大型数据集时可能会消耗较多内存。
  4. 在需要去重时选择 toSet() 而不是 toList()。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/35191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【threejs】火焰特效制作

2024-06-26 08-57-16火焰 shader 来源 //shadertory&#xff1a;https://www.shadertoy.com/view/ctVGD1//shadertory&#xff1a;https://www.shadertoy.com/view/ml3GWs 代码 import { DoubleSide, ShaderChunk, ShaderMaterial } from "three";export default fu…

华为OD机试【高矮个子排队】(java)(100分)

1、题目描述 现在有一队小朋友&#xff0c;他们高矮不同&#xff0c;我们以正整数数组表示这一队小朋友的身高&#xff0c;如数组{5,3,1,2,3}。 我们现在希望小朋友排队&#xff0c;以“高”“矮”“高”“矮”顺序排列&#xff0c;每一个“高”位置的小朋友要比相邻的位置高或…

利用BFS解决每个零售店到仓库最短距离之和问题

1、题目 矩阵中有3种类型&#xff1a;0仓库&#xff0c;-1障碍&#xff0c;1零售店。现在每个零售店要去距离它最近的仓库取货物&#xff0c;请计算出所有零售店到最近仓库距离之和&#xff0c;假设矩阵中每个单元格之间距离为1。如果遇到障碍物&#xff0c;则表示无法通过。可…

图神经网络实战(15)——SEAL链接预测算法

图神经网络实战&#xff08;15&#xff09;——SEAL链接预测算法 0. 前言1. SEAL 框架1.1 基本原理1.2 算法流程 2. 实现 SEAL 框架2.1 数据预处理2.2 模型构建与训练 小结系列链接 0. 前言 我们已经学习了基于节点嵌入的链接预测算法&#xff0c;这种方法通过学习相关的节点嵌…

2024年上半年软件设计师上午真题及答案解析

1.在计算机网络协议五层体系结构中&#xff0c;( B )工作在数据链路层。 A.路由器 B.以太网交换机 C.防火墙 D.集线器 网络层&#xff1a;路由器、防火墙 数据链路层&#xff1a;交换机、网桥 物理层&#xff1a;中继器、集线器 2.软件交付之后&#xff…

数据可视化期末考试(编程)

1.KNN 1.新增数据的分类 import pandas as pd # 您的原始数据字典 data { 电影名称: [电影1, 电影2, 电影3, 电影4, 电影5], 打斗镜头: [10, 5, 108, 115, 20], 接吻镜头: [110, 89, 5, 8, 200], 电影类型: [爱情片, 爱情片, 动作片, 动作片, 爱情片] } …

uni-app uni-data-picker级联选择器无法使用和清除选中的值

出现问题&#xff1a; 使用点击右边的叉号按钮无法清除已经选择的uni-data-picker值 解决办法&#xff1a; 在uni-app uni-data-picker使用中&#xff0c;要添加v-model&#xff0c;v-model在官网的示例中没有体现&#xff0c;但若不加则无法清除。 <uni-data-picker v-m…

OpenAI用GPT-4o打造癌症筛查AI助手;手机就能检测中风,准确率达 82%!中国气象局发布AI气象大模型...

AI for Science 企业动态速览—— * 皇家墨尔本大学用 AI 检测患者中风&#xff0c;准确率达 82% * OpenAI 用 GPT-4o 模型打造癌症筛查 AI 助手 * 中国气象局发布 AI 气象大模型风清、风雷、风顺 * AI 药企英矽智能&#xff1a;小分子抑制剂已完成中国 IIa 期临床试验全部患者…

GPT-5智能新纪元的曙光

在美国达特茅斯工程学院周四公布的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布&#xff0c;给出了肯定答案并表示将在一年半后发布。穆拉蒂在采访中还把GPT-4到GPT-5的飞跃描述为高中生到博士生的成长。 这一爆炸性的消息&#xff0c;震动了整体…

linux下进度条的实现

目录 一、代码一版 1.processbar.h 2.processbar.c 3.main.c 二、代码二版 1.processbar.h 2.processbar.c 3.main.c 三、改变文字颜色 一、代码一版 使用模块化编程 1.processbar.h #include<stdio.h> #define capacity 101 //常量使用宏定义 #define style…

代码随想录——买股票的最佳时机Ⅱ(Leecode122)

添加链接描述 贪心 局部最优&#xff1a;手机每天的正利润 全局最优&#xff1a;求最大利润 class Solution {public int maxProfit(int[] prices) {int res 0;for(int i 1; i < prices.length; i){res Math.max(prices[i] - prices[i - 1], 0);}return res;} }

webstorm无法识别tsconfig.json引用项目配置文件中的路径别名

问题 vite项目模板中&#xff0c;应用的ts配置内容写在tsconfig.app.json文件中&#xff0c;并在tsconfig.json通过项目引用的方式导入 {"files": [],"references": [{"path": "./tsconfig.app.json"},{"path": "./t…

2024年第十五届蓝桥杯青少组大赛8月24日开启

据蓝桥杯青少组官网显示&#xff0c;2024年第十五届蓝桥杯青少组大赛8月24日开启。 蓝桥杯青少组历届题库地址&#xff1a;http://www.6547.cn/question/cat/2 蓝桥杯青少组历届真题下载&#xff1a;http://www.6547.cn/wenku/list/10

统一视频接入平台LntonCVS视频共享交换平台智慧景区运用方案

随着夏季的到来&#xff0c;各地景区迎来了大量游客&#xff0c;而景区管理面临的挑战也愈加严峻&#xff0c;尤其是安全问题显得格外突出。 视频监控在预防各类安全事故方面发挥着重要作用&#xff0c;不论是自然景区还是人文景区&#xff0c;都潜藏着诸多安全隐患&#xff0…

排序之插入排序----直接插入排序和希尔排序(1)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 排序之插入排序----直接插入排序和希尔排序(1) 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨…

图形编辑器基于Paper.js教程04: Paper.js中的基础知识

背景 了解paper.js的基础知识&#xff0c;在往后的开发过程中会让你如履平地。 基础知识 paper.js 提供了两种编写方式&#xff0c;一种是纯粹的JavaScript编写&#xff0c;还有一种是使用官方提供的PaperScript。 区别就是在于&#xff0c;调用paper下的字对象是否需要加pa…

Windows server 由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开。

问题现象&#xff1a; 解决办法 临时远程方式1: 打开 mstsc 时带上 /admin 等参数&#xff0c;如下图所示&#xff1a; 使用“mstsc /admin /v:目标ip”来强制登录服务器&#xff0c;但只能是管理员身份。 远程方式2&#xff1a; 通过VM远程登陆系统后&#xff0c;运行输入R…

Python编程编辑器PyCharm 界面介绍

PyCharm 界面介绍 当你打开 PyCharm 后&#xff0c;会看到以下主要界面区域&#xff1a; 1&#xff09;菜单栏&#xff1a; 位于界面最顶端&#xff0c;包含文件&#xff08;File&#xff09;、编辑&#xff08;Edit&#xff09;、查看&#xff08;View&#xff09;、导航&a…

ICMAN液位检测——WS003B管道检测模组

ICMAN液位检测之WS003B管道检测模组 体积小&#xff0c;成本低&#xff0c; 液位检测精度高&#xff0c; 有水输出低电平无水高电平&#xff0c; 适用于饮水机、咖啡机、扫地机器人、洗地机等&#xff0c; 有需要朋友快联系我吧&#xff01; AWE展会不容错过的ICMAN检测模组…

RabbitMQ实践——搭建多人聊天服务

大纲 用户登录创建聊天室监听Stream&#xff08;聊天室&#xff09;发送消息实验登录Tom侧Jerry侧 创建聊天室Jerry侧Tom侧 进入聊天室Jerry侧Tom侧 发送消息Jerry发送消息Jerry侧聊天室Tom侧聊天室 Tom发送消息Jerry侧聊天室Tom侧聊天室 代码工程参考资料 在《RabbitMQ实践——…