当前位置: 首页 > news >正文

【Stream API】高效简化集合处理

 🔥个人主页: 中草药

 🔥专栏:【Java】登神长阶 史诗般的Java成神之路


 不可变集合

如果某个数据不能被修改,把他防御性地拷贝到不可变的集合是个很好的实现,在 Java 中,不可变集合(Immutable Collection) 是指一旦创建后,其内容(元素数量、元素值、结构)就无法被修改的集合。这意味着不能向其中添加、删除元素,也不能修改已有元素的引用(对于可变对象,其内部状态仍可能被修改)。

在List,Set,Map接口中,都存在静态的of方法,去获取一个不可变的集合

方法名称说明
static <E> List<E> of(E...elements)创建一个有指定元素的List集合对象
static <E> Set<E> of(E...elements)创建一个有指定元素的Set集合对象
static <K,V> Map<K,V> of(E...elements)创建一个有指定元素的Map集合对象

注意:Set和Map的元素不能重复

Map.of参数个数是有限的,最多只能创建10个键值对

两个可变参数不能共存

创建不可变的Map集合,参数个数超过10个要用以下方法

Map<String, String> map=new HashMap<>();map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
map.put("key5", "value5");
map.put("key6", "value6");Map.ofEntries(map.entrySet().toArray(new Entry[0])

原理是:toArray()方法 会比较集合长度和数组长度,如果集合长度>数组长度,此时会根据实际个数,重新创建数组,如果集合长度<=数组长度,此时数据在数组中放的下,可以直接用

在jdk10后引入了copyOf方法它的源码实现就是

测试Demo

import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class demo1 {public static void main(String[] args) {Map<String, String> map=new HashMap<>();map.put("key1", "value1");map.put("key2", "value2");map.put("key3", "value3");map.put("key4", "value4");map.put("key5", "value5");map.put("key6", "value6");//获取所有键值对对象Set<Map.Entry<String, String>> entries = map.entrySet();Map.Entry[] arr = new Map.Entry[0];//此时创建的是一个不可变的数组//toArray()方法 会比较集合长度和数组长度//如果集合长度>数组长度,此时会根据实际个数,重新创建数组//如果集合长度<=数组长度,此时数据在数组中放的下,可以直接用Map<Object, Object> staticMap = Map.ofEntries(entries.toArray(arr));//此方法会报错//staticMap.put("key7", "value7");//jdk10后Map<String, String> copyMap = Map.copyOf(map);copyMap.put("key1", "value1");}
}

 Sream流

Java 8引入了Stream API,它提供了一种高效且易于使用的数据处理方式,特别适合集合对象的操作,如过滤、映射、排序等。Stream API不仅可以提高代码的可读性和简洁性,还能利用多核处理器的优势进行并行处理。让我们通过两个具体的例子来感受下Java Stream API带来的便利,对比在Stream API引入之前的传统做法。

Stream 操作通常分为三个步骤:创建 Stream → 中间操作 → 终止操作

创建Stream流

获取方式方法名说明
单列集合default Stream<E> stream()Collection 中的默认方法
双列集合一般无法直接获取,一般需要先转化为可用的集合比如Map.EntrySet(),Map.KeySet()无法直接使用 stream 流
数组public static <T> Stream<T> stream(T[] array)Arrays 工具类中的静态方法
一堆零散数据public static<T> Stream<T> of(T... values)Stream 接口中的静态方法

Stream接口中的静态方法 of ,该方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组,但是数组必须是引用数据类型的,如果传递基本数据类型,会把整个数组当做一个元素放到Steam之中

public class demo3 {public static void main(String[] args) {int[] list1=new int[]{1,2,3,4,5};String[] list2=new String[]{"a","b","c","d","e"};List<Integer> list3=new ArrayList<>();Collections.addAll(list3,1,2,3,4,5);Stream.of(list1).forEach(a->System.out.println(a));}
}

中间方法

中间操作用于对 Stream 进行处理和转换,返回一个新的 Stream。多个中间操作可以链式调用,形成处理管道,且修改Stream流,原数组集合不会受到影响。

方法声明说明
Stream<T> filter(Predicate<? super T> predicate)过滤元素(满足 Predicate 条件的保留)
Stream<T> limit(long maxSize)获取流中前 maxSize 个元素
Stream<T> skip(long n)跳过流中前 n 个元素
Stream<T> distinct()元素去重(依赖元素的hashCode()equals()方法)
static <T> Stream<T> concat(Stream<T> a, Stream<T> b)合并两个 Stream 为一个新流
Stream<R> map(Function<T, R> mapper)转换流中元素的类型(由 Function 定义转换规则)
Stream<T> sorted()自然排序(元素需实现 Comparable 接口,按自然顺序排序)
Stream<T> peek(Consumer<? super T> action)对每个元素执行操作(如调试打印、修改元素内部状态等),返回原类型流

注意

测试demo

public class demo4 {public static void main(String[] args) {// 准备原始数据(包含重复元素、不同大小的整数)List<Integer> originalList = Arrays.asList(1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9);System.out.println("原始集合: " + originalList);// ====================== 1. filter 测试 ======================System.out.println("\n--- 1. filter (过滤出大于3的元素) ---");originalList.stream().filter(num -> num > 3).forEach(System.out::print);// ====================== 2. limit 测试 ======================System.out.println("\n--- 2. limit (获取前3个元素) ---");originalList.stream().limit(3).forEach(System.out::print);// ====================== 3. skip 测试 ======================System.out.println("\n--- 3. skip (跳过前3个元素) ---");originalList.stream().skip(3).forEach(System.out::print);// ====================== 4. distinct 测试 ======================System.out.println("\n--- 4. distinct (元素去重) ---");originalList.stream().distinct().forEach(System.out::print);// ====================== 5. concat 测试 ======================System.out.println("\n--- 5. concat (合并两个流) ---");Stream<Integer> streamA = Stream.of(10, 11);Stream<Integer> streamB = Stream.of(12, 13);Stream.concat(streamA, streamB).forEach(System.out::print);// ====================== 6. map 测试 ======================System.out.println("\n--- 6. map (转换元素类型:Integer -> String,添加前缀) ---");originalList.stream().map(num -> "数字:" + num).forEach(System.out::println);// ====================== 7. sorted 测试 ======================System.out.println("\n--- 7. sorted把顺序改为从大到小 ---");originalList.stream().sorted((a,b)->b-a).forEach(System.out::print);// ====================== 8. peek 测试 ======================System.out.println("\n--- 7. peek ---");List<Integer> result = originalList.stream().filter(n -> n % 2 == 0)       // 筛选偶数.peek(n -> System.out.println("过滤后的元素:" + n))  // 窥视中间结果.map(n -> n * 2)               // 乘以2.peek(n -> System.out.println("乘以2后的元素:" + n)) // 再次窥视.collect(Collectors.toList()); // 终端操作(触发执行)System.out.println("最终结果:" + result);// ====================== 链式编程演示(多个中间操作组合) ======================System.out.println("\n--- 链式编程:filter(偶数) -> distinct -> limit(2) -> map(加后缀) ---");originalList.stream().filter(num -> num % 2 == 0)   // 过滤偶数.distinct()                    // 去重.limit(2)                      // 取前2个.map(num -> num + "-偶数")      // 转换为带后缀的字符串.forEach(System.out::println);// ====================== 验证“不影响原集合” ======================System.out.println("\n--- 验证:修改Stream数据不会影响原集合 ---");System.out.println("原集合(修改前): " + originalList);// 在Stream中修改元素(乘以10)originalList.stream().map(num -> num * 10).forEach(modifiedNum -> System.out.println("Stream中修改后:" + modifiedNum));System.out.println("原集合(修改后): " + originalList); // 原集合无变化}
}

终止操作

方法声明作用说明特点
void forEach(Consumer action)遍历流中所有元素,对每个元素执行 action 操作(如打印、修改属性等)无返回值,纯粹的消费操作;并行流中可能乱序执行
long count()统计流中元素的数量返回 long 类型结果,是一个终端操作(执行后流关闭)
Object[] toArray()将流中元素收集到一个 Object 数组中返回类型固定为 Object[],如需指定类型需用重载方法 toArray(IntFunction)
collect(Collector collector)将流中元素收集到集合(如 ListSet)或其他容器中,支持复杂收集逻辑灵活性最高,可通过 Collectors 工具类实现多种收集需求(分组、拼接等)

注意:toArray()方法负责创建一个指定类型的数组

此外还有进行查找和匹配的方式进行终结

法声明作用说明特点
boolean allMatch(Predicate<T> predicate)判断流中 所有元素 是否都满足 predicate 指定的条件终端操作,支持 短路求值(发现不满足元素时立即终止遍历)
boolean anyMatch(Predicate<T> predicate)判断流中 是否存在至少一个元素 满足 predicate 指定的条件终端操作,支持 短路求值(发现满足元素时立即终止遍历)
boolean noneMatch(Predicate<T> predicate)判断流中 所有元素 是否都 不满足 predicate 指定的条件终端操作,支持 短路求值(发现满足元素时立即终止,返回 false
Optional<T> findFirst()返回流中 第一个元素(按遍历顺序),结果包装为 Optional<T>终端操作;顺序流中稳定返回首元素,并行流中也优先取首元素(但需注意并行遍历的顺序影响)
Optional<T> findAny()返回流中 任意一个元素,结果包装为 Optional<T>终端操作;并行流中效率更高(无需严格顺序),顺序流中通常返回首元素,但无严格保证
Optional<T> max(Comparator<T> comparator)
Optional<T> min(Comparator<T> comparator)
根据 comparator 规则,返回流中的 最大 / 最小元素,结果包装为 Optional<T>(空流时为 empty终端操作;需提供比较器,空流时返回 Optional.empty()

测试demo

import java.util.Arrays;
import java.util.List;public class demo6 {public static void main(String[] args) {List<Integer> originalList = Arrays.asList(1, 2, 2, 3, 4 , 6, 6,  8 );System.out.println("原始集合: " + originalList);System.out.println("allMatch");if(originalList.stream().skip(3).allMatch(x->x%2==0)){System.out.println("true");}else{System.out.println("false");}if(originalList.stream().skip(4).allMatch(x->x%2==0)){System.out.println("true");}else{System.out.println("false");}System.out.println();System.out.println("anyMatch");if(originalList.stream().skip(3).anyMatch(x->x%2!=0)){System.out.println("true");}else{System.out.println("false");}if(originalList.stream().skip(4).allMatch(x->x%2!=0)){System.out.println("true");}else{System.out.println("false");}System.out.println();System.out.println("findFirst");System.out.println(originalList.stream().skip(1).findFirst());System.out.println(originalList.stream().skip(2).findFirst());System.out.println();System.out.println("max min");System.out.println(originalList.stream().skip(1).max((a, b) -> a - b));System.out.println(originalList.stream().skip(1).max((a, b) -> b - a));}}

collect

collect是比较全能的终止操作,通过Collectors工具类可以将 Stream 转换为集合、Map 或进行分组等复杂操作。

测试demo-转化为list,set,map

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;public class demo7 {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13","张强-男-20", "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");ArrayList<String> list2 = new ArrayList<>();Collections.addAll(list2, "张无忌-男-15","张无忌-男-15", "周芷若-女-14", "赵敏-女-13","张强-男-20", "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");//目标提取男性的年龄List<String> collect = list1.stream().filter(x -> "男".equals(x.split("-")[1])).collect(Collectors.toList());System.out.println(collect);//转化为set可以去重Set<String> collect1 = list2.stream().filter(x -> "男".equals(x.split("-")[1])).collect(Collectors.toSet());System.out.println(collect1);//转化为mapMap<String, Integer> collect2 = list1.stream().filter(x -> "男".equals(x.split("-")[1])).collect(Collectors.toMap(new Function<String, String>() {@Overridepublic String apply(String s) {return s.split("-")[0];}},new Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s.split("-")[2]);}}));Map<String, Integer> collect3 = list1.stream().filter(x -> "男".equals(x.split("-")[1])).collect(Collectors.toMap(s -> s.split("-")[0],s -> Integer.parseInt(s.split("-")[2])));System.out.println(collect2);System.out.println(collect3);}
}

并行流

        并行流(ParallelStream)就是将源数据分为多个子流对象进行多线程操作,然后将处理的结果再汇总为一个流对象,底层是使用通用的 fork/join 池来实现,即将一个任务拆分成多个 “小任务” 并行计算,再把多个 “小任务” 的结果合并成总的计算结果,它是 Stream 实现并行计算的关键,它能自动利用多核处理器的优势,无需手动创建线程池。

ForkJoinPool 是 Java 中专为 “分而治之” 并行计算设计的线程池,其核心是将大任务拆分为可并行执行的小任务(Fork),待所有小任务完成后合并结果(Join);它通过 “工作窃取” 算法让空闲线程主动获取其他线程的任务,提高线程利用率,默认使用全局共享的公共池(并行度为 CPU 核心数 - 1),适合处理 CPU 密集型任务,不适合包含大量阻塞操作的场景。

使用并行流

只需将stream()替换为parallelStream(),或调用stream().parallel()

// 并行计算1到1000万的和
long sum = IntStream.rangeClosed(1, 10_000_000).parallel() // 转为并行流.sum();

注意事项

  • 避免在并行流中操作共享变量(可能导致线程安全问题)
  • 并非所有场景都适合并行流:小规模数据或计算密集型任务可能因线程开销反而变慢
  • 并行流使用Fork/Join框架,默认线程数为 CPU 核心数
  • 有序集合(如 List)的并行流会保持顺序,但会增加开销;无序集合可使用unordered()提升性能

要为重活而高兴,不要为死去的忧伤。                                                                ——林清玄

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸 

http://www.dtcms.com/a/362466.html

相关文章:

  • 智能数据采集实战:从爬取到分析一步到位
  • 【音视频】VP8 与 VP9 技术详解及与 H.264 H.265 的对比
  • FFmpeg命令行音视频工具:高效实现格式转换与批量处理,支持音频提取与精准视频剪辑
  • 【LeetCode牛客数据结构】单链表的应用——环形链表及链表分割问题详解
  • 【音视频】WebRTC-NACK
  • 苹果手机文本转音频,自行制作背诵素材
  • CentOS 创建站点
  • 从零开始学习C#上位机开发学习进阶路线,窥探工业自动化和物联网应用
  • Windows---DWORD与IPVOID
  • milvus-standalone启动失败unhealthy,错误日志context deadline exceeded
  • 2023知识图谱与大模型融合实践研究报告
  • Centos10虚拟机安装Zabbix
  • ELK 统一日志分析系统部署与实践指南(下)
  • cv610单独烧录 ddr失败
  • 一、Scala 基础语法、变量与数据类型
  • CentOS交换区处理
  • 第5节:分布式文件存储
  • Nginx 全攻略:从部署到精通的实战指南(CentOS 环境)
  • CentOS 7/8 单用户模式重置 root 密码完整流程
  • 解析ELK(filebeat+logstash+elasticsearch+kibana)日志系统原理以及k8s集群日志采集过程
  • 鸿蒙HarmonyOS应用开发者认证:抢占万物智联时代先机
  • C++之基于正倒排索引的Boost搜索引擎项目介绍
  • mac 本地安装maven环境
  • hadoop 框架 jar下载
  • Python面试题及详细答案150道(91-100) -- 迭代器与生成器篇
  • 大数据生态系统全景图:Hadoop、Spark、Flink、Hive、Kafka 的关系
  • 传统大数据 Hadoop 和 云原生湖仓 Databend 对比
  • 科学研究系统性思维的方法体系:数据分析方法
  • 【AI - nlp】Transformer输入部分要点
  • 3dmax烘培插件3dmax法线贴图烘焙教程glb和gltf元宇宙灯光效果图烘焙烘焙光影贴图支持VR渲染器