JavaSE : Stream 流操作

1. Stream的概念

1.1.什么是Stream

Java 8引入了Lambda表达式和Stream API,Stream代表一个由数据元素组成的序列,支持一系列如过滤、映射、聚合等高级操作,但不支持元素的增加和删除。

1.2.Stream与集合、数组的关系

Stream与集合(如List、Set)、数组等数据结构紧密相关,但又有所不同。集合和数组用于存储数据,而Stream则提供了一种访问和处理这些数据的途径。完成操作后,Stream不会改变原始数据源,而是产生一个新的结果或者副作用(例如打印输出)。

2. 创建Stream

2.1.从集合创建Stream

Collection.stream()

在Java中,几乎所有实现了Collection接口的集合类(如ListSet)都有一个stream()方法,该方法可以返回一个代表该集合元素序列的Stream。

List<String> list = Arrays.asList("Java", "Kotlin", "Scala");
Stream<String> stream = list.stream();
Collection.parallelStream()

并行流可以在多核处理器上利用多线程并行处理集合中的数据,从而在适当的情况下提高性能,特别是对于大数据集的处理。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
Stream<Integer> parallelStream = numbers.parallelStream();

2.2.从数组创建Stream

Stream API同样提供了从数组创建Stream的便捷方法。对于对象数组和基本数据类型数组,都有相应的方法来生成Stream。

数组对象

使用Arrays.stream(Object[] array)

String[] stringArray = new String[]{"Java", "Kotlin", "Scala"};
Stream<String> stringStream = Arrays.stream(stringArray);
基本数据类型数组

使用相应的方法,如IntStreamLongStreamDoubleStream

int[] intArray = new int[]{1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArray);

2.3.静态创建方法

Stream.of()

Stream.of()方法是一个静态方法,允许你直接从一个可变参数创建一个Stream。这个方法非常适合创建一个临时的Stream,特别是当需要快速生成一个包含几个元素的Stream时。

Stream<String> stream = Stream.of("Java", "Kotlin", "Scala");
IntStream.range( startInclusive, endExclusive )
  • startInclusive:范围的起始值,包含在流中。
  • endExclusive:范围的结束值,不包含在流中。
IntStream rangeStream = IntStream.range(1, 4); // 创建一个包含1, 2, 3的Stream

2.4.创建无限Stream

Java的Stream API还支持创建无限序列的Stream。这可以通过Stream.iterate()Stream.generate()方法实现。

创建无限Stream时需要特别小心,因为它们不会自然终止。通常,你需要使用limit()等操作来限制结果的数量,否则可能会导致程序长时间运行或内存溢出。

Stream.iterate()

从一个初始值开始,然后反复应用一个函数来生成后续的元素。

// 无限Stream,生成一个无限序列的随机数
Stream<Double> randomStream = Stream.generate(Math::random);
Stream.generate()

使用一个Supplier来生成每个元素。

// 无限Stream,从1开始,每次加1
Stream<Integer> infiniteStream = Stream.iterate(1, n -> n + 1);

2.5.其他特定类型

从文件创建
try (Stream<String> lines = Files.lines(Paths.get("path/to/file.txt"))) {lines.forEach(System.out::println);
} catch (IOException e) {e.printStackTrace();
}
从其他数据源创建

Java的NIO(New I/O)包提供了多种方式从不同数据源读取数据到Stream,如BufferedReader.lines()。对于更复杂的数据源,可能需要结合InputStreamReaderBufferedReader等类来构造Stream。

3.操作类型

3.1.中间操作

中间操作 (Intermediate Operations)用于处理数据,但它们不会直接产生结果。相反,它们返回一个新的Stream,允许进一步的操作链式调用。

这些操作是惰性求值的,即直到遇到终止操作时才执行。

筛选与切片
filter 过滤

filter(Predicate<T> predicate):根据提供的谓词过滤出满足条件的元素。

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
names.stream().filter(name -> name.length() > 4).forEach(System.out::println); // 输出: Charlie, David
distinct 去重复

distinct():去除重复元素。

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5);
numbers.stream().distinct().forEach(System.out::println); // 输出: 1, 2, 3, 4, 5
limit 取最多条

limit(long maxSize):限制Stream中元素的数量。

numbers.stream().distinct().limit(3).forEach(System.out::println); // 输出: 1, 2, 3
skip 跳过

skip(long n):跳过Stream开始的n个元素。

numbers.stream().distinct().skip(2).forEach(System.out::println); // 输出: 3, 4, 5
修改
peek 修改每个元素

peek():将每个元素修改值。

public class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() { return name; }public int getAge() { return age; }public void setName(String name){this.name = name;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return name + " (" + age + ")";}
}public class CustomSortExample {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("Alice", 30),new Person("Bob", 25),new Person("Charlie", 30),new Person("David", 25));List<Person> newPeoples = people.stream().peek(person->{person.setAge(person.getAge() + 10);}).collect(Collectors.toList());newPeoples.forEach(System.out::println);}
}
映射
map

map(Function<T, R> mapper):将每个元素转换为另一种形式或结构。

List<String> words = Arrays.asList("hello", "world");
words.stream().map(String::toUpperCase).forEach(System.out::println); // 输出: HELLO, WORLD

转换结构

  List<Map<String, Objcet>> peopleMap = people.stream().map(person->{Map<String, Objcet> map = new HashMap<>();map.put("aaa", person.getAge() );return map}).collect(Collectors.toList());peopleMap.forEach(System.out::println);
flatMap

flatMap(Function<T, Stream<R>> mapper):将每个元素转换为另一个Stream,然后将所有Stream连接成一个单一的Stream。

List<String> sentences = Arrays.asList("Hello world", "Java is fun");
sentences.stream().flatMap(sentence -> Arrays.stream(sentence.split(" "))).forEach(System.out::println); // 输出: Hello, world, Java, is, fun
排序
sorted自然顺序

sorted():自然排序,根据元素的自然顺序。

List<Integer> randomNumbers = Arrays.asList(5, 9, 1, 4, 7);
randomNumbers.stream().sorted().forEach(System.out::println); // 输出: 1, 4, 5, 7, 9
sorted 自定义排序

sorted(Comparator<T> comparator):自定义排序规则。

class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}public String getName() { return name; }public int getAge() { return age; }@Overridepublic String toString() {return name + " (" + age + ")";}
}public class CustomSortExample {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("Alice", 30),new Person("Bob", 25),new Person("Charlie", 30),new Person("David", 25));// 使用自定义比较器进行排序Comparator<Person> byAgeThenName = Comparator.comparing(Person::getAge).reversed() // 首先按年龄降序.thenComparing(Person::getName);      // 年龄相同则按姓名升序List<Person> sortedPeople = people.stream().sorted(byAgeThenName).collect(Collectors.toList());sortedPeople.forEach(System.out::println);}
}
并行处理与合并
parallel 并行流

parallel():将流转换为并行流。

randomNumbers.parallelStream().filter(num -> num % 2 == 0).forEach(System.out::println); // 结果顺序可能不同,因为是并行处理
sequential 换回顺序流

sequential():将流转换回顺序流,意味着之后的操作将在单线程中顺序执行,按照数据源的自然顺序或之前指定的排序进行。当不需要并行处理或者并行处理带来额外开销(如线程同步成本)大于其带来的性能提升时,可以使用此方法。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 假设这是之前并行处理的代码
numbers.parallelStream().map(n -> {// 模拟耗时操作Thread.sleep(100);return n * 2;}).sequential() // 转换为顺序流.forEach(System.out::println);
unordered 去除排序保证,允许优化

unordered():去除排序保证,允许优化。此方法去除了Stream的排序保证,允许某些操作(特别是并行操作)进行潜在的优化,因为不再需要维持元素的顺序。这对于那些不需要特定顺序就可以完成的操作来说,可能会提高效率。注意,只有在Stream来源本身没有固有排序(如HashSet转换的Stream)或通过无序操作(如unordered())创建的Stream上使用此方法才有意义。

List<String> words = Arrays.asList("Java", "Python", "C++", "Java", "Ruby");long javaCount = words.stream().unordered() // 允许无序处理以优化.filter(word -> word.equals("Java")).count();System.out.println("Count of 'Java': " + javaCount); // 输出:Count of 'Java': 2
基本类型转换
boxed() 原始类型流

在原始类型流的接口中,如IntStream, LongStream, DoubleStreamboxed() 方法用于将原始类型流转换为包装类型的对象流(如Stream<Integer>, Stream<Long>, Stream<Double>)。

IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
Stream<Integer> integerStream = intStream.boxed();
integerStream.forEach(System.out::println);

3.2.终止操作

终止操作 (Terminal Operations)会消费Stream,产生一个结果或副作用,并且执行后Stream不能再被使用。这些操作触发实际的计算。

查找与匹配
findFirst

findFirst():返回第一个元素(对于有序流有定义)。

Optional<String> first = words.stream().filter(word -> word.startsWith("h")).findFirst();
System.out.println(first.orElse("Not found")); // 输出: hello
anyMatch

anyMatch(Predicate<T> predicate):检查是否有至少一个元素满足条件。

boolean hasShortWord = words.stream().anyMatch(word -> word.length() < 5);
System.out.println(hasShortWord); // 输出: true
allMatch

allMatch(Predicate<T> predicate):检查是否所有元素都满足条件。

noneMatch

noneMatch(Predicate<T> predicate):检查是否没有元素满足条件。

归约
reduce

reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner):将流中的元素累积成一个单一的结果,常用于求和、乘积等。

Optional<Integer> sum = numbers.stream().reduce(Integer::sum);
System.out.println(sum.orElse(0)); // 输出: 15
收集
collect

collect(Collector<T, A, R> collector):将流转换为另一种数据结构或计算单一结果,如列表、集合、映射等。

  • Collectors.toList()Collectors.toSet()

    List<String> upperCaseWords = words.stream().map(String::toUpperCase).collect(Collectors.toList());
    System.out.println(upperCaseWords); // 输出: [HELLO, WORLD]
    
  • Collectors.toMap()

    基本应用:键值一对一映射

    import java.util.*;
    import java.util.stream.*;class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}public String getName() { return name; }public int getAge() { return age; }
    }public class ToMapExample {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("Alice", 30),new Person("Bob", 25),new Person("Charlie", 30));Map<String, Person> mapByName = people.stream().collect(Collectors.toMap(Person::getName, p -> p));mapByName.forEach((name, person) -> System.out.println(name + ": " + person.getAge()));}
    }
    

    处理键冲突:合并函数

    Map<String, Integer> mapWithMerge = people.stream().collect(Collectors.toMap(Person::getName, Person::getAge, (oldValue, newValue) -> oldValue + newValue // 合并函数,这里简单相加处理冲突));mapWithMerge.forEach((name, ageSum) -> System.out.println(name + ": " + ageSum));
    

    如果有同名的人,他们的年龄会被相加

groupingBy

groupingBy() 是 Java Stream API 中的一个收集器(Collector),它用于将流中的元素按照某个属性或者函数的结果进行分组。下面我将通过一个例子来说明如何使用 groupingBy() 方法。

假设我们有一个列表,里面存储的是员工(Employee)对象,每个员工有姓名(name)和部门(department)两个属性。现在我们想按部门对员工进行分组,可以这样做:

首先,定义一个简单的 Employee 类:

public class Employee {private String name;private String department;// 构造方法、getter和setter省略...
}

然后,创建一个员工列表并使用 groupingBy() 进行分组:

import java.util.*;
import java.util.stream.Collectors;public class GroupingExample {public static void main(String[] args) {List<Employee> employees = Arrays.asList(new Employee("Alice", "HR"),new Employee("Bob", "IT"),new Employee("Charlie", "HR"),new Employee("David", "Finance"),new Employee("Eva", "IT"));// 使用groupingBy按部门分组Map<String, List<Employee>> employeesByDepartment = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));// 打印结果for (Map.Entry<String, List<Employee>> entry : employeesByDepartment.entrySet()) {System.out.println("Department: " + entry.getKey());for (Employee employee : entry.getValue()) {System.out.println("  Employee: " + employee.getName());}}}
}

在这个例子中,Collectors.groupingBy(Employee::getDepartment) 表示根据员工的部门属性进行分组,最终得到的结果是一个 Map<String, List<Employee>>,其中键是部门名称,值是该部门的所有员工列表。

输出结果将会是按部门分组的员工列表,类似于这样:

Department: HREmployee: AliceEmployee: Charlie
Department: ITEmployee: BobEmployee: Eva
Department: FinanceEmployee: David

这个例子展示了如何使用 groupingBy() 来对集合中的对象进行分组处理,是一个非常实用且强大的Stream API特性。

partitioningBy

partitioningBy()

聚合统计
count

count():返回流中元素的数量。

long evenCount = numbers.stream().filter(num -> num % 2 == 0).count();
System.out.println(evenCount); // 输出: 3
min

min(Comparator<? super T> comparator)max(Comparator<? super T> comparator):找出最小或最大元素。

average

average():计算平均值(仅适用于数值类型流)。

连接字符串
joining

Collectors.joining(CharSequence delimiter):在收集器中使用,连接流中的元素为一个字符串。

String.join(CharSequence delimiter, CharSequence... elements):静态方法,非Stream操作,但常与Stream结合使用来连接字符串。

String joinedWords = words.stream().collect(Collectors.joining(", "));
System.out.println(joinedWords); // 输出: hello, world
输出
forEach

forEach(Consumer<? super T> action):对流中的每个元素执行给定操作。

numbers.stream().forEach(num -> System.out.println("Number: " + num));
// 输出: Number: 1, Number: 2, ... (按顺序)
forEachOrdered

forEachOrdered(Consumer<? super T> action):与forEach相似,但在顺序流中保证元素的遍历顺序。

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

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

相关文章

Find My OBD|苹果Find My技术与OBD结合,智能防丢,全球定位

OBD是英文On-Board Diagnostics的缩写&#xff0c;中文翻译为“车载自动诊断系统”。这个系统将从发动机的运行状况随时监控汽车是否尾气超标&#xff0c;一旦超标&#xff0c;会马上发出警示。当系统出现故障时&#xff0c;故障(MIL)灯或检查发动机(Check Engine)警告灯亮&…

配置ubuntu的VNC时遇到报错_XSERVTransmkdir: Mode of /tmp/.X11-unix should be set to 1777

现在win11内嵌了ubuntu系统&#xff0c;我在根据打造基于 VNC 的 Ubuntu 20.04 的远程桌面 配置VNC server时&#xff0c;到了 vncserver :1 这一步&#xff0c;遇到报错&#xff1a; vncserver: /usr/bin/Xtigervnc did not start up, please look into /root/.vnc/xxxxx.:1.…

电脑压缩图片怎么压缩?简单高效的压缩技巧

当我们将一些图片上传到各大网站的时候&#xff0c;经常会被提示图片大小超过网站的限制而被禁止上传&#xff0c;其实这是网站的一种防御措施&#xff0c;防止大量体积太大的图片占用了网站服务器的存储空间&#xff0c;从而影响用户体验&#xff0c;所以为了解决这个问题&…

vivo上下而求索

为一台手机&#xff0c;消费者能期待一整年。今天&#xff0c;在数码圈能有这种待遇的品牌&#xff0c;也许只有两个&#xff1a;苹果&#xff0c;以及vivo。 5月13日晚&#xff0c;vivo召开“影像新蓝图暨X系列新品发布会”&#xff0c;正式带来vivo X100s、vivo X100s Pro和v…

纯血鸿蒙APP实战开发——阅读翻页方式案例

介绍 本示例展示手机阅读时左右翻页&#xff0c;上下翻页&#xff0c;覆盖翻页的功能。 效果图预览 使用说明 进入模块即是左右翻页模式。点击屏幕中间区域弹出上下菜单。点击设置按钮&#xff0c;弹出翻页方式切换按钮&#xff0c;点击可切换翻页方式。左右翻页方式可点击翻…

python高级爱心代码

python高级爱心代码实现&#xff1a; import turtle import random # 设置画布 screen turtle.Screen() screen.bgcolor("black") # 创建画笔 pen turtle.Turtle() pen.speed(0) pen.color("red") pen.penup() # 移动画笔到起始位置 pen.goto(0, -20…

构建企业的多分支网络,你可以有这些选择

为企业构建稳定、灵活的网络&#xff0c;是企业IT人员非常重要的基础工作之一。对于多分支企业而言&#xff0c;总部与各分支之间需要进行数据互联和监管&#xff0c;所以大多面临组网需求。多分支企业组网是指企业总部与分公司、工厂、门店等多点之间的网络组建&#xff0c;不…

SNRO

号码范围(number range)创建及应用 | 摆渡SAP

白鹿以前的短视频:四川京之华锦信息技术公司

白鹿以前的短视频&#xff1a;时光剪影中的创意火花与成长轨迹 在短视频风起云涌的互联网时代&#xff0c;每一位创作者的早期作品都如同时间胶囊&#xff0c;封存着最初的创意与梦想。提及“白鹿”这一名字&#xff0c;不少人心中会立刻浮现出那个凭借独特风格和不懈努力&…

AI 一键生成高清短视频,视频 UP 主们卷起来...

现在短视频越来越火&#xff0c;据统计&#xff0c;2023年全球短视频用户数量已达 10 亿&#xff0c;预计到2027年将突破 24 亿。对于产品展示和用户营销来说&#xff0c;短视频已经成为重要阵地&#xff0c;不管你喜不喜欢它&#xff0c;你都得面对它&#xff0c;学会使用它。…

这些网站我愿称之为做答辩PPT的神

很多快要毕业的同学在做答辩PPT的时候总是感觉毫无思路&#xff0c;一窍不通。但这并不是你们的错&#xff0c;对于平时没接触过相关方面&#xff0c;第一次搞答辩PPT的人来说&#xff0c;这是很正常的一件事。一个好的答辩PPT可以根据以下分为以下几部分来写。 1.研究的背景和…

安防视频汇聚/智能分析云平台EasyCVR调用localfile接口会返回日志的问题该如何解决?

视频汇聚/安防视频融合云平台EasyCVR视频监控系统支持多协议接入、兼容多类型设备&#xff0c;平台能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理。视频监控/集中存储系统EasyCVR平台可支持国…

前端面试题日常练-day10 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末。 1. 下面哪个CSS属性用于设置元素的字体样式&#xff1f; a) font-size b) font-color c) font-style d) font-weight2. 如何在JavaScript中判断一个变量的类型&#xff1f; a) typeOfb) getTypec)…

差分数组算法

举例 考虑数组 a [ 1 , 3 , 3 , 5 , 8 ] a[1,3,3,5,8] a[1,3,3,5,8]&#xff0c;对其中的相邻元素两两作差&#xff08;右边减左边&#xff09;&#xff0c;得到数组 [ 2 , 0 , 2 , 3 ] [2,0,2,3] [2,0,2,3]。然后在开头补上 a [ 0 ] a[0] a[0]&#xff0c;得到差分数组 d …

DBeaver配置离线驱动

因为部署的服务器为无网环境&#xff0c;所以在服务器上使用DBeaver需要配置离线驱动 我们在有网的环境下&#xff0c;安装DBeaver。把驱动下载下来&#xff0c;然后再拷贝到没网的设备上 一、下载驱动 1.在有网的设备上&#xff0c;打开DBeaver 2.找到窗口&#xff0c;选择…

使用 Gin-Docs 自动生成 API 文档

该插件移植自 Python 的 Flask-Docs&#xff0c;可以根据代码注释生成文档页面&#xff0c;支持离线文档下载和生成&#xff0c;支持在线调试&#xff0c;支持密码认证。 Gin-Docs Gin API 文档自动生成插件 特性 根据代码注释自动生成 Markdown 文档支持离线 Markdown 文档下…

面向服务架构设计理论与实践

SOA 与微服务的区别 &#xff08;1&#xff09;微服务相比于 SOA 更加精细&#xff0c;微服务更多地以独立的进程的方式存在&#xff0c;互相之间并无影响。 &#xff08;2&#xff09;微服务提供的接口方式更加通用化&#xff0c;例如 HTTP RESTful 方式&#xff0c;各种终端…

SAP BSEG VS ACDOCA 差异

温习一下 ACDOCA VS BSEG matinal&#xff1a;S4 HANA 详解ACDOCA与BSEG的区别都在这了_sap acdoca-CSDN博客

【PDF技巧】PDF如何解密?

PDF文件设置了加密&#xff0c;需要密码才能够打开文件或者编辑文件&#xff0c;那么如何解密PDF密码&#xff1f;今天我们来一起学习一下。 首先是在已知密码的情况下&#xff0c;PDF文件中的打开密码或者是限制编辑&#xff0c;想要解密PDF密码&#xff0c;我们只需要在PDF编…