Java 8 引入了 Stream API,它为集合类提供了一种声明式的处理机制。Stream 不是数据结构,而是数据载体,提供了用于处理数据源(如:Collection、Arrays 或 I/O channels)的流式操作。它允许你以一种声明式的方式处理集合数据。Stream 就像是一个迭代器(Iterator),但它可以支持并行处理,并且提供了丰富的操作函数,使得对集合的操作更加简洁和高效。
Stream 的主要特点是:
Stream 的工作原理可以分为三个步骤:
Stream 的操作分为中间操作和终端操作。中间操作会返回一个新的 Stream,它可以被链式调用,比如filter、map等;终端操作会触发 Stream 的处理,并返回一个结果或副作用,比如forEach、collect等。Stream 的处理是延迟的,只有在终端操作被调用时,中间操作才会真正执行,这种特性被称为 “惰性求值”。
可以通过多种方式创建 Stream:
Collection.stream() 和 Collection.parallelStream(): 从集合中创建流或并行流。
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();
String[] fruits = {"apple", "banana", "orange"};
Stream<String> stream = Arrays.stream(fruits);
Stream<String> stream = Stream.of("apple", "banana", "orange");
中间操作不会立即执行,它们只是构建了一个流水线,直到遇到终端操作才会真正执行。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 输出: [2, 4, 6]
List<String> words = Arrays.asList("hello", "world");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// 输出: [5, 5]
List<List<Integer>> lists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4)
);
List<Integer> flatList = lists.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// 输出: [1, 2, 3, 4]
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 输出: [1, 2, 3]
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 输出: [1, 1, 3, 4, 5, 9]
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.peek(System.out::println)
.map(n -> n * 2)
.collect(Collectors.toList());
// 输出: 1, 2, 3, 4, 5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> skippedNumbers = numbers.stream()
.skip(3)
.collect(Collectors.toList());
// skippedNumbers 为 [4, 5, 6]
终端操作会触发流的执行并产生结果或副作用。
List<String> words = Arrays.asList("hello", "world");
words.stream().forEach(System.out::println);
// 输出: hello, world
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubled = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
// 输出: [2, 4, 6, 8, 10]
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream().reduce(Integer::sum);
System.out.println(sum.orElse(0));
// 输出: 15
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();
System.out.println(count);
// 输出: 5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);
System.out.println(hasEven);
// 输出: true
List<Integer> numbers = Arrays.asList(2, 4, 6, 8);
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
System.out.println(allEven);
// 输出: true
List<Integer> numbers = Arrays.asList(1, 3, 5, 7);
boolean noEven = numbers.stream().noneMatch(n -> n % 2 == 0);
System.out.println(noEven);
// 输出: true
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> first = numbers.stream().findFirst();
System.out.println(first.orElse(null));
// 输出: 1
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> any = numbers.parallelStream().findAny();
System.out.println(any.orElse(null));
// 输出: 可能是任意一个元素
假设我们有一个包含用户信息的列表,需要筛选出年龄大于 18 岁的用户,并将他们的名字转换为大写。
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 17),
new User("Charlie", 30)
);
List<String> adultNames = users.stream()
.filter(user -> user.getAge() > 18)
.map(User::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(adultNames);
// 输出: [ALICE, CHARLIE]
假设我们需要读取一个文件,统计其中每个单词出现的次数。
try (Stream<String> lines = Files.lines(Paths.get("input.txt"))) {
Map<String, Long> wordCounts = lines.flatMap(line -> Arrays.stream(line.split("\\s+")))
.filter(word -> !word.isEmpty())
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(wordCounts);
} catch (IOException e) {
e.printStackTrace();
}