1.定义
Java Stream 是 Java 8 引入的一个新特性,用于处理集合数据的操作。
Java Stream不是数据结构,是一种对数据进行操作和计算的方式。
提供一种声明性的方法来处理数据,类似于 SQL 语句。
2.关键点
声明性:使用 Stream API 可以用更少的代码、更清晰的逻辑来处理集合数据。
链式操作:Stream 操作可以链式调用,形成一条操作流水线。
惰性求值:Stream 的中间操作是惰性求值的,只有在终端操作时才会执行。
并行处理:Stream API 支持并行处理,可以利用多核处理器的优势。
3.流的分类
Java Stream 流可以根据操作类型和特性进行分类
3.1 按数据源分类
顺序流
(Sequential Stream)按顺序处理数据,一个接一个地处理。就像排队买票,每个人按顺序一个接一个地买票。
import java.util.Arrays;
import java.util.List;
public class SequentialStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用顺序流
numbers.stream()
.map(n -> n * n)
.forEach(System.out::println);
}
}
输出
1
4
9
16
25
并行流
(Parallel Stream)并行处理数据,多个数据可以同时处理。就像在多个窗口同时买票,每个人可以在不同的窗口同时买票。
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用并行流
numbers.parallelStream()
.map(n -> n * n)
.forEach(System.out::println);
}
}
输出(顺序可能不同)
1
4
9
16
25
3.2 按操作类型分类
中间操作
(Intermediate Operations):返回一个新的 Stream,可以链式调用。中间操作是惰性求值的,只有在终端操作时才会执行。
filter
:过滤元素map
:映射每个元素到一个新的元素flatMap
:将每个元素转换为一个流,然后将这些流合并成一个流sorted
:对元素进行排序distinct
:去重limit
:截取前 N 个元素skip
:跳过前 N 个元素
List<String> list = Arrays.asList("a", "b", "c", "a");
list.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.distinct()
.sorted()
.forEach(System.out::println);
解释:
创建流:list.stream() 创建了一个顺序流。
过滤操作:filter(s -> s.startsWith("a")) 过滤出以 "a" 开头的字符串来,保留下来以 "a" 开头的字符串,结果是 ["a", "a"]。
映射操作:map(String::toUpperCase) 将每个字符串转换为大写,结果是 ["A", "A"]。
去重操作:distinct() 去除重复的元素,结果是 ["A"]。
排序操作:sorted() 对元素进行排序,结果仍然是 ["A"](因为只有一个元素,不需要排序)。
终端操作:forEach(System.out::println) 打印每个元素。
最终输出:A
终端操作
(Terminal Operations):触发流的计算,返回一个结果。
forEach
:对每个元素执行操作collect
:将流转换为其他形式reduce
:将流中的元素组合成一个值count
:计算流中元素的个数anyMatch
:检查是否至少有一个元素匹配给定的条件allMatch
:检查是否所有元素都匹配给定的条件noneMatch
:检查是否所有元素都不匹配给定的条件findFirst
:返回第一个元素findAny
:返回任意一个元素
ist<String> list = Arrays.asList("a", "b", "c", "a");
long count = list.stream()
.filter(s -> s.startsWith("a"))
.count();
System.out.println(count);
解释:
创建流:list.stream() 创建了一个顺序流。
过滤操作:filter(s -> s.startsWith("a")) 过滤出以 "a" 开头的字符串。
输入列表是 ["a", "b", "c", "a"]。
过滤后,结果是 ["a", "a"],因为只有这两个字符串以 "a" 开头。
计数操作:count() 计算流中元素的个数。
过滤后的结果是 ["a", "a"],所以元素个数是 2。
输出结果:System.out.println(count) 打印计数结果。
最终输出:2
3.3 按数据类型分类
IntStream:处理 int 类型的流。
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// 创建一个包含 1 到 5 的 IntStream
IntStream intStream = IntStream.rangeClosed(1, 5);
// 计算流中所有元素的和
int sum = intStream.sum();
System.out.println("Sum: " + sum); // 输出: Sum: 15
// 创建一个新的 IntStream 并打印每个元素
IntStream.rangeClosed(1, 5).forEach(System.out::println);
// 输出:
// 1
// 2
// 3
// 4
// 5
}
}
LongStream:处理 long 类型的流。
import java.util.stream.LongStream;
public class LongStreamExample {
public static void main(String[] args) {
// 创建一个包含 1 到 5 的 LongStream
LongStream longStream = LongStream.rangeClosed(1, 5);
// 计算流中所有元素的和
long sum = longStream.sum();
System.out.println("Sum: " + sum); // 输出: Sum: 15
// 创建一个新的 LongStream 并打印每个元素
LongStream.rangeClosed(1, 5).forEach(System.out::println);
// 输出:
// 1
// 2
// 3
// 4
// 5
}
}
DoubleStream:处理 double 类型的流。
import java.util.stream.DoubleStream;
public class DoubleStreamExample {
public static void main(String[] args) {
// 创建一个包含 1.0 到 5.0 的 DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0, 4.0, 5.0);
// 计算流中所有元素的和
double sum = doubleStream.sum();
System.out.println("Sum: " + sum); // 输出: Sum: 15.0
// 创建一个新的 DoubleStream 并打印每个元素
DoubleStream.of(1.0, 2.0, 3.0, 4.0, 5.0).forEach(System.out::println);
// 输出:
// 1.0
// 2.0
// 3.0
// 4.0
// 5.0
}
}
4.获取流的常用方式
4.1 从集合获取流
List、Set 等集合可以直接调用 stream()
方法获取流。
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
4.2 从数组获取流
使用 Arrays.stream()
方法将数组转换为流。
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
4.3 使用 Stream.of() 方法
直接使用 Stream.of()
方法创建
Stream<String> stream = Stream.of("a", "b", "c");
4.4 从文件获取流
使用 Files.lines()
方法读取文件并将其转换为流。
try (Stream<String> stream = Files.lines(Paths.get("file.txt"))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
5.常用方法用法
5.1 遍历 / 匹配(foreach
、find
、match
)
forEach
方法用于遍历流中的每个元素并执行指定的操作。
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
find
方法用于查找流中满足条件的第一个元素。
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
Optional<String> foundName = names.find(name -> name.startsWith("A"));
match
方法用于判断流中的元素是否满足指定的条件。
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
boolean allStartWithA = names.allMatch(name -> name.startsWith("A"));
5.2 聚合(max
、min
、count
)
max
方法用于获取流中的最大值。
Stream<Integer> numbers = Stream.of(1, 5, 3, 7, 2);
Optional<Integer> maxNumber = numbers.max(Integer::compareTo);
min
方法用于获取流中的最小值。
Stream<Integer> numbers = Stream.of(1, 5, 3, 7, 2);
Optional<Integer> minNumber = numbers.min(Integer::compareTo);
count
方法用于计算流中元素的数量。
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
long count = names.count();
5.3 映射(map
、flatMap
)
map
方法用于将流中的每个元素按照指定的函数进行映射转换。
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
Stream<Integer> nameLengths = names.map(name -> name.length());
flatMap
方法用于将流中的每个元素转换为一个流,并将这些流扁平化合并为一个新的流。
Stream<List<String>> lists = Stream.of(List.of("A", "B"),
List.of("C", "D"));
Stream<String> flattened = lists.flatMap(list -> list.stream());
5.4 归约(reduce
)
reduce
方法用于对流中的元素进行归约操作,将多个元素合并为一个结果。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.reduce((a, b) -> a + b);
5.5 收集(collect
)
collect
方法用于将流中的元素收集到一个新的集合或其他数据结构中。
归集(toList
、toSet
、toMap
)
tream<String> names = Stream.of("Alice", "Bob", "Charlie");
List<String> nameList = names.collect(Collectors.toList());
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
Set<String> nameSet = names.collect(Collectors.toSet());
Stream<Person> people = Stream.of(new Person("Alice", 20),
new Person("Bob", 30));
Map<String, Integer> nameAgeMap = people.collect(Collectors.toMap(Person::getName, Person::getAge));
统计(count
、averaging
)
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Double average = numbers.collect(Collectors.averagingInt(num -> num));
分组(partitioningBy
、groupingBy
)
Stream<Person> people = Stream.of(new Person("Alice", 20), new Person("Bob", 30), new Person("Charlie", 20));Map<Boolean, List<Person>> partitioned = people.collect(Collectors.partitioningBy(person -> person.getAge() > 25));
Stream<Person> people = Stream.of(new Person("Alice", 20), new Person("Bob", 30), new Person("Charlie", 20));Map<Integer, List<Person>> grouped = people.collect(Collectors.groupingBy(Person::getAge));
接合(joining
)
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");String joinedNames = names.collect(Collectors.joining(", "));
排序(sorted
)
Stream<String> names = Stream.of("Charlie", "Alice", "Bob");Stream<String> sortedNames = names.sorted();
5.6 去重、合并(distinct
、skip
、limit
)
distinct
方法用于去除流中的重复元素。
Stream<Integer> numbers = Stream.of(1, 2, 2, 3, 3, 3);Stream<Integer> distinctNumbers = numbers.distinct();
skip
方法用于跳过流中的前 n
个元素。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);Stream<Integer> skippedNumbers = numbers.skip(2);
limit
方法用于获取流中的前 n
个元素。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);Stream<Integer> limitedNumbers = numbers.limit(3);