【Springboot8】Java Stream 流详解
看了月神的文章,放在我这里,方便复习浏览
一、Stream 流概述
以前写集合处理:要写很多 for 循环、代码冗长
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> res = new ArrayList<>();
for(int x : list){
if(x % 2 == 0){
res.add(x * 2);
}
}
Stream 的目标
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> res = list.stream()
.filter(x -> x % 2 == 0) // 过滤偶数
.map(x -> x * 2) // 每个数 *2
.collect(Collectors.toList());
效果:
//filter(过滤)
list.stream()
.filter(x -> x > 3)
//map(转换)
.map(x -> x * 2)
//sorted(排序)
.sorted()
//forEach(遍历)
list.stream().forEach(System.out::println);
//collect(收集结果)
.collect(Collectors.toList())
//reduce(累加/汇总
int sum = list.stream()
.reduce(0, (a, b) -> a + b);
Stream 流是 Java 8 中引入的一个重要新特性,它允许我们以声明式的方式处理集合数据。Stream 流就像是一个流水线,我们可以在这个流水线上对数据进行一系列的操作,比如过滤、映射、排序等,最终得到我们想要的结果。
与传统的集合遍历方式相比,Stream 流具有代码简洁、可读性高、易于并行处理等优点。它不会改变原始集合的数据,所有的操作都是在流上进行的,并且只有在执行终止操作时,中间操作才会真正执行,这就是所谓的 “惰性求值”。
二、Stream 流的创建
1. 通过集合创建
在 Java 8 中,Collection 接口新增了 stream () 方法,用于返回一个顺序流;还新增了 parallelStream () 方法,用于返回一个并行流。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreateDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
// 获取顺序流
Stream<String> stream = list.stream();
// 获取并行流
Stream<String> parallelStream = list.parallelStream();
}
}
这个示例很简单,我们先创建了一个存储字符串的 List 集合,然后分别通过 stream () 和 parallelStream () 方法获取了顺序流和并行流。顺序流是单线程处理数据,并行流则是多线程处理数据,在处理大量数据时,并行流可以提高处理效率。
2. 通过数组创建
Arrays 类提供了 stream () 方法,可以将数组转换为 Stream 流。
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamCreateDemo2 {
public static void main(String[] args) {
String[] strArray = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(strArray);
}
}
这里我们创建了一个字符串数组,然后使用 Arrays.stream () 方法将其转换为了 Stream 流,非常直观易懂。
3. 通过 Stream 的 of () 方法创建
Stream 类的 of () 方法可以直接传入元素来创建 Stream 流。例如,Stream.of(“苹果”, “香蕉”, “橘子”); 这样就创建了一个包含这三个字符串元素的Stream流,无需先构建集合或数组,特别适合少量元素的快速创建场景。
import java.util.stream.Stream;
public class StreamCreateDemo3 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("苹果", "香蕉", "橘子");
}
}
在这个示例中,我们直接将 “苹果”、“香蕉”、“橘子” 这几个元素传入 Stream.of () 方法,就创建了一个包含这些元素的 Stream 流,操作非常简便。
三、Stream 流的中间操作
中间操作是指对 Stream 流进行处理后,返回一个新的 Stream 流的操作。中间操作不会立即执行,只有当终止操作执行时,中间操作才会真正执行。常见的中间操作有过滤、映射、排序等。
1. 过滤(filter)
filter () 方法用于过滤出满足指定条件的元素,它接收一个 Predicate 函数式接口作为参数。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamFilterDemo {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 过滤出大于3的数字
Stream<Integer> filterStream = numbers.stream().filter(n -> n > 3);
// 终止操作:输出结果
filterStream.forEach(System.out::println); // 输出4、5
}
}
在这个示例中,我们创建了一个包含 1 到 5 的整数集合,然后通过 filter () 方法过滤出大于 3 的数字。这里的n -> n > 3是一个 Lambda 表达式,作为 Predicate 接口的实现,用于判断元素是否满足条件。最后通过 forEach () 这个终止操作输出结果,可以看到只输出了 4 和 5。
2. 映射(map)
map () 方法用于将流中的元素映射到另一个元素,可以将一种类型的流转换为另一种类型的流,它接收一个 Function 函数式接口作为参数。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamMapDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("王五");
// 将名字转换为名字的长度
Stream<Integer> nameLengthStream = names.stream().map(name -> name.length());
// 终止操作:输出结果
nameLengthStream.forEach(System.out::println); // 输出2、2、2
}
}
我们有一个存储名字的集合,通过 map () 方法将每个名字映射为它的长度,这里的name -> name.length()是一个 Lambda 表达式,实现了 Function 接口,将 String 类型的名字转换为 Integer 类型的长度。最后输出的结果都是 2,因为这三个名字都是两个字。
3. 排序(sorted)
sorted () 方法用于对 Stream 流中的元素进行排序。它有两种形式,一种是无参的,用于对实现了 Comparable 接口的元素进行自然排序;另一种是有参的,接收一个 Comparator 接口作为参数,用于自定义排序规则。
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
public class StreamSortedDemo {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(1);
numbers.add(2);
// 自然排序
System.out.println("自然排序结果:");
numbers.stream().sorted().forEach(System.out::println); // 输出1、2、3
List<String> words = new ArrayList<>();
words.add("banana");
words.add("apple");
words.add("orange");
// 自定义排序(按字符串长度排序)
System.out.println("按字符串长度排序结果:");
words.stream().sorted(Comparator.comparingInt(String::length)).forEach(System.out::println); // 输出apple、orange、banana
}
}
在第一个示例中,我们对整数集合进行自然排序,sorted () 方法无参,直接按照整数的自然顺序排序,结果是 1、2、3。在第二个示例中,我们对字符串集合按照字符串长度进行排序,通过Comparator.comparingInt(String::length)自定义了排序规则,apple 长度为 5,orange 长度为 6,banana 长度为 6,所以输出结果为 apple、orange、banana。
四、Stream 流的终止操作
终止操作会触发流的处理,并产生一个结果或副作用,一旦执行了终止操作,流就不能再被使用了。常见的终止操作有 forEach、count、collect 等。
1. forEach
forEach () 方法用于遍历流中的元素,对每个元素执行指定的操作,它接收一个 Consumer 函数式接口作为参数。
import java.util.ArrayList;
import java.util.List;
public class StreamForEachDemo {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橘子");
// 遍历输出每个水果
fruits.stream().forEach(fruit -> System.out.println(fruit));
// 输出:苹果、香蕉、橘子
}
}
这个示例很简单,我们创建了一个水果集合,通过 forEach () 方法遍历流中的每个水果,并将其输出。fruit -> System.out.println(fruit)这个 Lambda 表达式实现了 Consumer 接口,用于消费流中的元素。
2. count
count () 方法用于返回流中元素的个数,返回值是一个 long 类型。
import java.util.ArrayList;
import java.util.List;
public class StreamCountDemo {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 统计元素个数
long count = numbers.stream().count();
System.out.println("元素个数为:" + count); // 输出5
}
}
我们创建了一个包含 5 个整数的集合,通过 count () 方法统计流中元素的个数,最后输出结果为 5,非常直观地展示了 count () 方法的用法。
3. collect
collect () 方法用于将流中的元素收集到一个集合或其他数据结构中,它接收一个 Collector 接口的实现作为参数。Java 8 的 Collectors 工具类提供了很多常用的 Collector 实现。
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class StreamCollectDemo {
public static void main(String[] args) {
List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("Java");
languages.add("C++");
// 将流收集到List集合中
List<String> list = languages.stream().collect(Collectors.toList());
System.out.println("List集合:" + list); // 输出[Java, Python, Java, C++]
// 将流收集到Set集合中(去重)
Set<String> set = languages.stream().collect(Collectors.toSet());
System.out.println("Set集合:" + set); // 输出[Java, Python, C++]
}
}
我们有一个存储编程语言的集合,其中 “Java” 出现了两次。通过 collect (Collectors.toList ()) 将流收集到 List 集合中,得到的 List 包含了所有元素,包括重复的 “Java”。而通过 collect (Collectors.toSet ()) 将流收集到 Set 集合中,由于 Set 集合不允许重复元素,所以得到的 Set 集合中 “Java” 只出现了一次。
五、综合示例
下面我们通过一个综合示例来运用前面所学的 Stream 流的知识,实现对一个员工列表的处理。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
// 员工类
class Employee {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "Employee{name='" + name + "', age=" + age + ", salary=" + salary + '}';
}
}
public class StreamComprehensiveDemo {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("张三", 25, 5000));
employees.add(new Employee("李四", 30, 7000));
employees.add(new Employee("王五", 28, 6000));
employees.add(new Employee("赵六", 35, 9000));
employees.add(new Employee("孙七", 22, 4000));
// 需求:筛选出年龄大于25岁的员工,将他们的工资增加1000,然后按工资从高到低排序,最后收集到List集合中
List<Employee> result = employees.stream()
.filter(employee->employee.getAge() > 25) // 筛选年龄大于25岁的员工
.map(employee-> new Employee(employee.getName(), employee.getAge(), employee.getSalary()+1000)) // 工资增加1000
.sorted(Comparator.comparingDouble(Employee::getSalary).reversed()) // 按工资从高到低排序
//.sorted((e1, e2) -> Double.compare(e2.getSalary(), e1.getSalary())) // 按工资从高到低排序
.toList(); // 收集到List集合中
// 输出结果
result.forEach(System.out::println);
// 输出:
// Employee{name='赵六', age=35, salary=10000.0}
// Employee{name='李四', age=30, salary=8000.0}
// Employee{name='王五', age=28, salary=7000.0}
}
}
在这个综合示例中,我们首先创建了一个员工列表,每个员工有姓名、年龄和工资属性。然后我们通过一系列的 Stream 流操作来满足需求:首先使用 filter () 方法筛选出年龄大于 25 岁的员工;接着使用 map () 方法为这些员工增加 1000 工资;再使用 sorted () 方法按照工资从高到低进行排序;最后使用 collect () 方法将结果收集到 List 集合中。通过 forEach () 方法输出结果,可以看到符合条件的员工信息按照我们的要求进行了处理。
更多推荐




所有评论(0)