公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。

在本教程中,您将通过大量示例来学习 Java 8 Stream API。

Java 在 Java 8 中提供了一个新的附加包,称为 java.util.stream。该包由类、接口和枚举组成,允许对元素进行函数式操作。 您可以通过在程序中导入 java.util.stream包来使用流。

Stream提供以下功能:

Stream不存储元素。它只是通过计算操作的管道传送来自数据结构、数组或 I/O 通道等源的元素。

Stream本质上是函数式的,对流执行的操作不会修改其源。例如,过滤从集合获取的 Stream 会生成一个没有过滤元素的新 Stream,而不是从源集合中删除元素。

Stream是惰性的,仅在需要时才计算代码,在流的生命周期中,流的元素仅被访问一次。

与迭代器一样,必须生成新流才能重新访问源中的相同元素。

您可以使用 Stream 来 过滤、收集、打印以及 从一种数据结构转换为其他数据结构等。

Stream API 示例

1. 创建一个空的Stream

在创建空流时,应使用 empty() 方法:

Stream<String> stream = Stream.empty();
stream.forEach(System.out::println);

通常情况下,在创建时会使用 empty() 方法,以避免在没有元素的流中返回 null:

public Stream<String> streamOf(List<String> list) {
return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}

2.从集合中创建流

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream; public class StreamCreationExamples {
public static void main(String[] args) throws IOException { Collection<String> collection = Arrays.asList("JAVA", "J2EE", "Spring", "Hibernate");
Stream<String> stream2 = collection.stream();
stream2.forEach(System.out::println); List<String> list = Arrays.asList("JAVA", "J2EE", "Spring", "Hibernate");
Stream<String> stream3 = list.stream();
stream3.forEach(System.out::println); Set<String> set = new HashSet<>(list);
Stream<String> stream4 = set.stream();
stream4.forEach(System.out::println);
}
}

输出

JAVA
J2EE
Spring
Hibernate
JAVA
J2EE
Spring
Hibernate
JAVA
Hibernate
J2EE
Spring

3. 从数组中创建流对象

数组可以是流的源,也可以从现有数组或数组的一部分创建数组:

import java.util.Arrays;
import java.util.stream.Stream; public class StreamCreationExample {
public static void main(String[] args) {
// 使用Arrays.stream()创建流
int[] numbers = {1, 2, 3, 4, 5};
Stream<Integer> stream1 = Arrays.stream(numbers);
System.out.println("Using Arrays.stream():");
stream1.forEach(System.out::println); // 使用Stream.of()创建流
String[] names = {"Alice", "Bob", "Charlie"};
Stream<String> stream2 = Stream.of(names);
System.out.println("Using Stream.of():");
stream2.forEach(System.out::println); // 使用Stream.builder()创建流
String[] colors = {"Red", "Green", "Blue"};
Stream.Builder<String> builder = Stream.builder();
for (String color : colors) {
builder.add(color);
}
Stream<String> stream3 = builder.build();
System.out.println("Using Stream.builder():");
stream3.forEach(System.out::println);
}
}

输出

Using Arrays.stream():
1
2
3
4
5
Using Stream.of():
Alice
Bob
Charlie
Using Stream.builder():
Red
Green
Blue

4. 使用Stream过滤一个集合示例

在下面的示例中,我们不使用流过滤数据,看看代码是什么样的,同时我们在给出一个使用stream过滤的示例,对比一下

不使用Stream过滤一个集合示例

import java.util.ArrayList;
import java.util.List; public class FilterWithoutStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50); List<Integer> filteredNumbers = new ArrayList<>();
for (Integer number : numbers) {
if (number > 30) {
filteredNumbers.add(number);
}
} System.out.println("Filtered numbers (without Stream):");
for (Integer number : filteredNumbers) {
System.out.println(number);
}
}
}

输出:

Filtered numbers (without Stream):
40
50

使用 Stream 过滤集合示例:

import java.util.ArrayList;
import java.util.List; public class FilterWithStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50); List<Integer> filteredNumbers = numbers.stream()
.filter(number -> number > 30)
.toList(); System.out.println("Filtered numbers (with Stream):");
filteredNumbers.forEach(System.out::println);
}
}

输出:

Filtered numbers (with Stream):
40
50

前后我们对比一下,可以看到,使用 Stream 进行集合过滤可以更加简洁和直观,减少了手动迭代和添加元素的步骤。它提供了一种声明式的编程风格,使代码更易读、可维护和可扩展。

5. 使用Stream过滤和遍历集合

在下面的示例中,我们使用 filter() 方法进行过滤,使用 forEach() 方法对数据流进行迭代:

import java.util.ArrayList;
import java.util.List; public class FilterAndIterateWithStreamExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("David");
names.add("Eve"); System.out.println("Filtered names starting with 'A':");
names.stream()
.filter(name -> name.startsWith("A"))
.forEach(System.out::println);
}
}

输出

Filtered names starting with 'A':
Alice

在上述示例中,我们有一个字符串列表 names,其中包含了一些名字。

我们使用 Stream 进行过滤和迭代操作以查找以字母 "A" 开头的名字。

6.使用Collectors方法求和

我们还可以使用Collectors计算数值之和。

在下面的示例中,我们使用Collectors类及其指定方法计算所有产品价格的总和。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; public class SumByUsingCollectorsMethods {
public static void main(String[] args) {
List < Product > productsList = new ArrayList < Product > ();
productsList.add(new Product(1, "HP Laptop", 25000f));
productsList.add(new Product(2, "Dell Laptop", 30000f));
productsList.add(new Product(3, "Lenevo Laptop", 28000f));
productsList.add(new Product(4, "Sony Laptop", 28000f));
productsList.add(new Product(5, "Apple Laptop", 90000f)); double totalPrice3 = productsList.stream()
.collect(Collectors.summingDouble(product -> product.getPrice()));
System.out.println(totalPrice3); }
}

输出

201000.0

7. 使用Stream查找年龄最大和最小的学生

假设有一个 Student 类具有 name 和 age 属性。我们可以使用 Stream 来查找学生集合中年龄的最大和最小值,并打印出相应的学生信息。以下是一个示例:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional; public class StudentStreamExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20));
students.add(new Student("Bob", 22));
students.add(new Student("Charlie", 19));
students.add(new Student("David", 21)); // 查找年龄最大的学生
Optional<Student> maxAgeStudent = students.stream()
.max(Comparator.comparingInt(Student::getAge)); // 查找年龄最小的学生
Optional<Student> minAgeStudent = students.stream()
.min(Comparator.comparingInt(Student::getAge)); // 打印最大和最小年龄的学生信息
System.out.println("Student with maximum age:");
maxAgeStudent.ifPresent(System.out::println); System.out.println("Student with minimum age:");
minAgeStudent.ifPresent(System.out::println);
}
} class Student {
private String name;
private int age; public Student(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public int getAge() {
return age;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

输出:

Student with maximum age:
Student{name='Bob', age=22}
Student with minimum age:
Student{name='Charlie', age=19}

8. 使用Stream转换List为Map

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; public class StudentStreamToMapExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20));
students.add(new Student("Bob", 22));
students.add(new Student("Charlie", 19));
students.add(new Student("David", 21)); // 将学生列表转换为 Map,以姓名为键,学生对象为值
Map<String, Student> studentMap = students.stream()
.collect(Collectors.toMap(Student::getName, student -> student)); // 打印学生 Map
for (Map.Entry<String, Student> entry : studentMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
} class Student {
private String name;
private int age; public Student(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public int getAge() {
return age;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

输出

David: Student{name='David', age=21}
Bob: Student{name='Bob', age=22}
Charlie: Student{name='Charlie', age=19}
Alice: Student{name='Alice', age=20}

在上面示例中,我们使用Collectors.toMap() 方法将学生列表转换为 Map。我们指定了键提取器 Student::getName,将学生的姓名作为键。对于值提取器,我们使用了一个匿名函数 student -> student,它返回学生对象本身作为值。

9. 使用Stream把List对象转换为另一个List对象

假设我们有一个 Person 类,其中包含姓名和年龄属性。我们可以使用 Stream 来将一个 List 对象转换为另一个 List 对象,其中只包含人员的姓名。以下是一个示例:

  import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors; public class ListTransformationExample {
public static void main(String[] args) {
List<Person> persons = new ArrayList<>();
persons.add(new Person("Alice", 20));
persons.add(new Person("Bob", 22));
persons.add(new Person("Charlie", 19)); // 将 Person 列表转换为只包含姓名的 String 列表
List<String> names = persons.stream()
.map(Person::getName)
.collect(Collectors.toList()); // 打印转换后的姓名列表
System.out.println(names);
}
} class Person {
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public int getAge() {
return age;
}
}

输出:

  [Alice, Bob, Charlie]

在上述示例中,我们有一个 Person 类,其中包含姓名和年龄属性。我们创建了一个 persons 列表,并添加了几个 Person 对象。

使用Stream,我们通过调用 map() 方法并传入一个方法引用 Person::getName,最后,我们使用 collect() 方法和 Collectors.toList() 将转换后的姓名收集到一个新的列表中。

API

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

Java Stream中的API你都用过了吗?的更多相关文章

  1. java stream中Collectors的用法

    目录 简介 Collectors.toList() Collectors.toSet() Collectors.toCollection() Collectors.toMap() Collectors ...

  2. Java项目中每一个类都可以有一个main方法

    Java项目中每一个类都可以有一个main方法,但只有一个main方法会被执行,其他main方法可以对类进行单元测试. public class StaticTest { public static ...

  3. Java Stream API入门篇

    本文github地址 你可能还没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少类就清楚了.Java 8之所以费这么大功夫引入函数式编程,原因有二: 代码简洁,函数式编程写 ...

  4. 深度掌握 Java Stream 流操作,让你的代码高出一个逼格!

    概念 Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选.排序.聚合等. Stream 的操作符大体上分为两种:中间操作符和终止操作符 中 ...

  5. Java反射库中的安全漏洞在30个月后终于修复了(转)

    2013年7月,安全组织Security Explorations发现了Java 7u25中的一个安全漏洞,通过这个漏洞攻击者可以完全摆脱Java沙箱.Oracle在更新的7u40中包含了一个补丁,但 ...

  6. Java基础中的一些注意点

    1.在Java编程语言中,标识符是赋予变量.类或方法的名称.标识符可从一个字母.下划线(_)或美元符号($)开始,随后也可跟数字.标识符是大小写区别对待的并且未规定最大长度. 2.Java技术源程序采 ...

  7. XML在JAVA项目中的作用

    java项目中,xml文件一般都是用来存储一些配置信息 一般的编程, 多数用来存储配置信息 . 拿JDBC来说,可以把数据库连接字符串写到xml,如果要修改数据源,只需要改xml就可以了,没必要再去重 ...

  8. 【Java学习笔记之二十二】解析接口在Java继承中的用法及实例分析

    一.定义 Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为( ...

  9. 【JVM】Java 8 中的常量池、字符串池、包装类对象池

    1 - 引言 2 - 常量池 2.1 你真的懂 Java的“字面量”和“常量”吗? 2.2 常量和静态/运行时常量池有什么关系?什么是常量池? 2.3 字节码下的常量池以及常量池的加载机制 2.4 是 ...

  10. Java 8 中的 Streams API 详解

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

随机推荐

  1. [docker]安装常见数据库

    前言 本文使用docker安装常见数据库大部分没配置什么参数,只是基本的安装. 不只是数据库,还有elasticsearch.rabbitmq等和数据相关的服务. docker 版本: 18.06.3 ...

  2. Docker使用教程及常用命令

    Docker是一个开源的应用容器引擎,允许开发者将应用以及依赖打包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows上.它非常适用于持续集成与持续交付(CI/CD). 1. 安装 ...

  3. 让C#调用vue组件里的方法

    前言:web页面开发时采用的是vue开发的,后台语言是C# 需求:后台需要通过浏览器调用vue组件的方法 c# 可以调用xxx.html 中的script引用的js中定义的方法是可以调用的, 之前c# ...

  4. Controller 层代码技巧

    Controller 层代码技巧 前言 本篇主要要介绍的就是controller层的处理,一个完整的后端请求由4部分组成: 接口地址(也就是URL地址) 请求方式(一般就是get.post,当然还有p ...

  5. 百亿补贴通用H5导航栏方案

    背景 在移动端页面中,由于屏幕空间有限,导航条扮演着非常重要的角色,提供了快速导航到不同页面或功能的方式.用户也通常会在导航条中寻找他们感兴趣的内容,因此导航条的曝光率较高.在这样的背景下,提供一个动 ...

  6. 2D KD-Tree实现

    KD-tree 1.使用背景 在项目中遇到一个问题: 如何算一个点到一段折线的最近距离~折线的折点可能有上千个, 而需要检索的点可能出现上万的数据量, 的确是个值得思考的问题~ 2.暴力解法 有个比较 ...

  7. Python colorama 设置控制台、命令行输出彩色文字

    为了方便调试代码,经常会向stdout中输出一些日志,但是大量日志,有时不好定位问题. 使用终端打印特定颜色字符串,可以突出显示关键性的信息,帮助用户更好地识别和理解输出内容. https://pyp ...

  8. 【RocketMQ】【源码】延迟消息实现原理

    RocketMQ设定了延迟级别可以让消息延迟消费,延迟消息会使用SCHEDULE_TOPIC_XXXX这个主题,每个延迟等级对应一个消息队列,并且与普通消息一样,会保存每个消息队列的消费进度(dela ...

  9. dedebiz友情链接样式修改

    文件位置 /system/taglib/flink.lib.php 45行位置 根据自己需求修改就行

  10. iptables和firewalld

    iptables简介 iptables不是一个单一的软件工具,而是一套c/s样式的软件组,它是由工作在用户空间的iptables和工作在内核空间的vetilter模块组成,一般统称为Iptables. ...