Java Stream 流详解
JDK 8 中的 Stream 流是一个处理集合数据的新方式。它允许以声明性方式操作数据,支持并行处理,提供了丰富的操作方法如映射、过滤、排序等,可以极大简化集合处理的代码。
Stream 流具有以下特点:
- 不是数据结构: 它不存储数据,而是提供了一种流水线操作数据的方法。
- 函数式编程风格: 可以通过链式调用操作方法,如
map
、filter
、reduce
等,更加灵活和简洁。 - 惰性求值: 只有在终止操作调用时才会真正执行流水线操作,这样可以避免不必要的计算。
- 支持并行处理: 可以轻松地使用并行流进行并行处理,提高处理大数据集合的效率。
Stream 流的使用可以显著简化代码,提高代码的可读性和维护性,是 JDK 8 引入的重要特性之一。
一、常用特点
JDK 8 中 Stream 流的常用特点包括:
- 有序性(Order):
- 流保持其元素的有序性,除非显式地使用无序操作。
- 比如,从列表生成的流会保持列表的顺序。
- 惰性求值(Lazy Evaluation):
- 中间操作(如
filter
、map
)是惰性求值的,只有在终止操作(如collect
、forEach
)调用时才会真正执行。 - 这样可以优化性能,避免不必要的计算。
- 中间操作(如
- 无存储(No Storage):
- 流本身不存储数据,它从底层的数据结构、数组或 I/O 通道等数据源中获取数据。
- 数据源可以是集合、数组、I/O 频道等。
- 不可变性(Immutability):
- 流操作不会修改其数据源中的元素,而是生成新的流。
- 这种不可变性有助于并行处理和线程安全。
- 支持并行(Parallelism):
- 流可以轻松转换为并行流,使用多核处理器加速处理。
- 通过调用
parallelStream()
或parallel()
方法。
- 支持聚合操作(Aggregation Operations):
- Stream API 提供了丰富的聚合操作,如
reduce
、collect
等,可以方便地对数据进行聚合计算。
- Stream API 提供了丰富的聚合操作,如
- 支持无限流(Infinite Streams):
- 流可以是有限的也可以是无限的。无限流可以使用
generate
或iterate
方法创建。 - 处理无限流时通常需要使用限制操作如
limit
。
- 流可以是有限的也可以是无限的。无限流可以使用
- 可组合(Composable):
- 流的中间操作可以组合起来形成复杂的数据处理流水线。
- 通过链式调用实现数据过滤、转换、排序等操作。
二、基本概念与操作
(一)、创建Stream
以下是几种常见的创建 Stream 的方法:
1. 从集合创建 【常用】
可以通过集合(如 List、Set)创建 Stream。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamFromCollection {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
}
}
2. 从数组创建【常用】
可以通过数组创建 Stream。
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamFromArray {
public static void main(String[] args) {
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
stream.forEach(System.out::println);
// 对于基本类型数组,可以使用特定的流类型
int[] intArray = {1, 2, 3};
IntStream intStream = Arrays.stream(intArray);
intStream.forEach(System.out::println);
}
}
3. 使用 Stream.of 方法【常用】
可以使用 Stream.of
方法直接创建 Stream。
import java.util.stream.Stream;
public class StreamOf {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);
// 创建空流
Stream<String> emptyStream = Stream.empty();
emptyStream.forEach(System.out::println); // 无输出
}
}
4. 使用 Stream.generate 方法
可以使用 Stream.generate
方法创建无限流。
在Java的流(Stream)API中,你可以使用 Stream.generate()
或者 Stream.iterate()
方法来创建无限流。这些方法允许你提供一个生成元素的函数,并且不受固定大小的限制。例如,可以使用 Stream.generate(Math::random)
来生成一个无限的随机数流,或者使用 Stream.iterate(0, n -> n + 1)
来生成一个从0开始的整数无限流。
处理无限流时需要特别注意,通常需要使用流操作(如 .limit(n)
)来限制元素数量,否则可能会导致无限循环或内存耗尽的问题。无限流的特性使得它们在某些情况下非常有用,例如在模拟、数学运算或者需要动态生成数据时。
import java.util.stream.Stream;
public class StreamGenerate {
public static void main(String[] args) {
Stream<Double> stream = Stream.generate(Math::random).limit(5);
stream.forEach(System.out::println);
}
}
5. 使用 Stream.iterate 方法【常用】
可以使用 Stream.iterate
方法生成具有特定规则的无限流。
import java.util.stream.Stream;
public class StreamIterate {
public static void main(String[] args) {
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
stream.forEach(System.out::println);
}
}
6. 从文件创建【常用】
可以使用 Files.lines
方法从文件中读取所有行作为流。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class StreamFromFile {
public static void main(String[] args) {
try (Stream<String> stream = Files.lines(Paths.get("path/to/file.txt"))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. 从其他数据源创建
Stream 还可以从其他数据源创建,例如通过 BufferedReader
的 lines
方法从 I/O 流中创建,或者通过 Pattern.splitAsStream
从正则表达式分割结果中创建。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class StreamFromOtherSources {
public static void main(String[] args) throws IOException {
// 从 BufferedReader 创建 Stream
String text = "Hello\\nWorld\\nStream";
BufferedReader reader = new BufferedReader(new StringReader(text));
Stream<String> streamFromReader = reader.lines();
streamFromReader.forEach(System.out::println);
// 从正则表达式分割结果创建 Stream
String textToSplit = "apple,banana,orange";
Stream<String> streamFromPattern = Pattern.compile(",").splitAsStream(textToSplit);
streamFromPattern.forEach(System.out::println);
}
}
除了上述提到的方式,还有一些特定的创建 Stream 的方法和技巧:
8. 使用 Stream.builder 方法
可以使用 Stream.builder
创建一个可变的 Stream。
import java.util.stream.Stream;
public class StreamBuilder {
public static void main(String[] args) {
Stream.Builder<String> builder = Stream.builder();
builder.add("a")
.add("b")
.add("c");
Stream<String> stream = builder.build();
stream.forEach(System.out::println);
}
}
9. 使用 Stream.concat 方法
可以使用 Stream.concat
方法将两个 Stream 连接起来。
import java.util.stream.Stream;
public class StreamConcat {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("a", "b");
Stream<String> stream2 = Stream.of("c", "d");
Stream<String> concatenatedStream = Stream.concat(stream1, stream2);
concatenatedStream.forEach(System.out::println);
}
}
10. 使用 Pattern.splitAsStream 方法
可以使用 Pattern.splitAsStream
方法从正则表达式分割结果中创建 Stream。
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class StreamSplitAsStream {
public static void main(String[] args) {
String text = "apple,banana,orange";
Stream<String> stream = Pattern.compile(",").splitAsStream(text);
stream.forEach(System.out::println);
}
}
11. 从 Optional 创建
可以使用 Optional.stream
方法从 Optional 对象中创建一个包含零个或一个元素的 Stream。
import java.util.Optional;
import java.util.stream.Stream;
public class StreamFromOptional {
public static void main(String[] args) {
Optional<String> optional = Optional.of("value");
Stream<String> stream = optional.stream();
stream.forEach(System.out::println);
}
}
12. 自定义实现 Stream
在某些情况下,可能需要自定义实现 Stream。这通常涉及实现 Spliterator
接口来定义迭代器行为,然后使用 StreamSupport.stream
方法创建 Stream。
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class CustomStream {
public static void main(String[] args) {
// 自定义 Spliterator
Spliterator<String> spliterator = new Spliterator<String>() {
private String[] elements = {"a", "b", "c"};
private int index = 0;
@Override
public boolean tryAdvance(Consumer<? super String> action) {
if (index < elements.length) {
action.accept(elements[index++]);
return true;
}
return false;
}
@Override
public Spliterator<String> trySplit() {
return null; // 不支持分割
}
@Override
public long estimateSize() {
return elements.length - index;
}
@Override
public int characteristics() {
return ORDERED | SIZED | SUBSIZED;
}
};
// 使用 StreamSupport 创建 Stream
Stream<String> stream = StreamSupport.stream(spliterator, false);
stream.forEach(System.out::println);
}
}
(二)中间操作
1. filter
filter
用于根据指定的条件筛选流中的元素,保留符合条件的元素。
解释:
filter
方法接受一个Predicate
函数式接口,即一个返回boolean
值的 lambda 表达式或方法引用。- 对流中的每个元素应用该谓词,如果返回
true
,则该元素会被保留在结果流中,否则会被过滤掉。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
List<String> filteredList = list.stream()
.filter(s -> s.startsWith("a"))
.collect(Collectors.toList());
System.out.println(filteredList); // 输出: [apple]
}
}
2. map
map
用于将流中的每个元素转换为另一种类型的元素。
解释:
map
方法接受一个Function
函数式接口,即一个接收一个参数并返回一个结果的 lambda 表达式或方法引用。- 对流中的每个元素应用该函数,将其转换为另一种类型的新元素,并将这些新元素组成一个新的流。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange");
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); // 输出: [5, 6, 6]
}
}
3. flatMap
flatMap
用于将流中的每个元素转换为一个流,然后将这些流合并为一个新的流。
解释:
flatMap
方法接受一个Function
函数式接口,该接口将每个元素转换为一个流。- 这些流将被扁平化为一个单一的流。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f")
);
List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList); // 输出: [a, b, c, d, e, f]
}
}
4. distinct
distinct
用于去除流中的重复元素,只保留唯一的元素。
解释:
distinct
方法使用元素的equals
方法来判断元素是否重复。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class DistinctExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctList); // 输出: [1, 2, 3, 4, 5]
}
}
5. sorted
sorted
用于对流中的元素进行排序。
解释:
sorted
方法有两个变体,一个不带参数,默认按自然顺序排序;另一个带参数,可以传递一个Comparator
函数式接口以指定排序规则。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class SortedExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "orange", "banana", "pear");
List<String> sortedList = list.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedList); // 输出: [apple, banana, orange, pear]
// 自定义排序
List<String> reverseSortedList = list.stream()
.sorted((s1, s2) -> s2.compareTo(s1))
.collect(Collectors.toList());
System.out.println(reverseSortedList); // 输出: [pear, orange, banana, apple]
}
}
6. limit
limit
用于截取流中的前 N 个元素。
解释:
limit
方法接受一个long
类型的参数,表示要截取的元素个数。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LimitExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedList = list.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println(limitedList); // 输出: [1, 2, 3]
}
}
7. skip
skip
用于跳过流中的前 N 个元素。
解释:
skip
方法接受一个long
类型的参数,表示要跳过的元素个数。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class SkipExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedList = list.stream()
.skip(2)
.collect(Collectors.toList());
System.out.println(skippedList); // 输出: [3, 4, 5]
}
}
8. peek
peek
用于对流中的每个元素执行操作并返回一个新的流,该操作通常用于调试。
解释:
peek
方法接受一个Consumer
函数式接口,即一个接收一个参数并不返回结果的 lambda 表达式或方法引用。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class PeekExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "orange");
List<String> resultList = list.stream()
.peek(s -> System.out.println("Processing: " + s))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(resultList); // 输出: [APPLE, BANANA, ORANGE]
}
}
9. takeWhile
(Java 9 及以上版本中可用)
takeWhile
按照条件从流中获取元素,直到条件为 false 时停止。
解释:
takeWhile
方法接受一个Predicate
函数式接口,即一个返回boolean
值的 lambda 表达式或方法引用。- 对流中的每个元素应用该谓词,直到遇到返回
false
的元素时停止获取。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class TakeWhileExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> resultList = list.stream()
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(resultList); // 输出: [1, 2, 3]
}
}
10. dropWhile
(Java 9 及以上版本中可用)
dropWhile
按照条件丢弃元素,直到条件为 false 时开始获取剩余的元素。
解释:
dropWhile
方法接受一个Predicate
函数式接口,即一个返回boolean
值的 lambda 表达式或方法引用。- 对流中的每个元素应用该谓词,直到遇到返回
false
的元素时开始获取剩余的元素。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class DropWhileExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> resultList = list.stream()
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(resultList); // 输出: [4, 5, 6]
}
}
11. flatMapToInt
、flatMapToDouble
、flatMapToLong
这些方法用于将元素转换为对应的基本类型流。
解释:
flatMapToInt
、flatMapToDouble
和flatMapToLong
方法分别用于将元素转换为IntStream
、DoubleStream
和LongStream
。- 接受一个函数,将元素转换为基本类型的流。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class FlatMapToIntExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("1", "2", "3");
IntStream intStream = list.stream()
.flatMapToInt(s -> IntStream.of(Integer.parseInt(s)));
intStream.forEach(System.out::println); // 输出: 1 2 3
}
}
12. mapToInt
、mapToDouble
、mapToLong
这些方法用于将元素映射为对应的基本类型流。
解释:
mapToInt
、mapToDouble
和mapToLong
方法分别用于将元素映射为IntStream
、DoubleStream
和LongStream
。- 接受一个函数,将元素转换为对应的基本类型。
示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class MapToIntExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("1", "2", "3");
IntStream intStream = list.stream()
.mapToInt(Integer::parseInt);
intStream.forEach(System.out::println); // 输出: 1 2 3
}
}
(三)、终止操作
下面是一些常见的Stream 流中的终止操作,包括其功能、使用场景和示例代码。
1. forEach
forEach
是用于遍历流中的每一个元素,并对每个元素执行指定的操作。通常用于打印或更新元素。
功能
- 对流中的每个元素执行一个动作。
- 该动作通常是一个
Consumer
接口的实现。
使用场景
- 遍历并打印流中的元素。
- 对流中的元素执行某种操作,如更新状态。
示例代码
import java.util.Arrays;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "def", "ghi");
// 使用 forEach 打印每个元素
strings.stream()
.forEach(System.out::println);
}
}
2. collect
collect
是用于将流中的元素收集到一个结果容器中,如 List、Set 或 Map。它是流中最灵活和最强大的终止操作之一。
功能
- 将流中的元素聚集到一个容器中(如
List
、Set
或Map
)。 - 使用
Collector
进行收集操作,Collectors
工具类提供了大量预定义的收集器。
使用场景
- 将流转换为其他数据结构(如 List、Set)。
- 对流中的数据进行分组、分区等复杂操作。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class CollectExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 过滤空字符串并收集结果到 List
List<String> filteredList = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
// 将字符串收集到 Set 中
Set<String> stringSet = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toSet());
System.out.println("Filtered List: " + filteredList);
System.out.println("Set: " + stringSet);
}
}
3. reduce
reduce
是用于将流中的元素组合成一个单一的值。常见的使用场景包括求和、计算乘积、连接字符串等。
功能
- 将流中的元素通过累加器函数组合成一个值。
- 可以有一个初始值,或者返回一个
Optional
值。
使用场景
- 累加、乘积等聚合操作。
- 将流中的元素按某种规则合并。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算所有数的和
int sum = numbers.stream()
.reduce(0, Integer::sum);
// 计算所有数的乘积
Optional<Integer> product = numbers.stream()
.reduce((a, b) -> a * b);
System.out.println("Sum: " + sum);
product.ifPresent(result -> System.out.println("Product: " + result));
}
}
4. count
count
是用于计算流中元素的个数,返回一个 long
类型的值。
功能
- 计算流中元素的数量。
使用场景
- 统计流中元素的个数。
示例代码
import java.util.Arrays;
import java.util.List;
public class CountExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 计算非空字符串的个数
long count = strings.stream()
.filter(string -> !string.isEmpty())
.count();
System.out.println("Count: " + count);
}
}
5. anyMatch
、allMatch
、noneMatch
这些方法用于判断流中的元素是否满足指定的条件,返回值是 boolean
类型。
功能
anyMatch
:流中是否有任意一个元素满足条件。allMatch
:流中是否所有元素都满足条件。noneMatch
:流中是否所有元素都不满足条件。
使用场景
- 用于验证流中元素是否符合特定条件。
示例代码
import java.util.Arrays;
import java.util.List;
public class MatchExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "def", "ghi");
// 是否存在以 "a" 开头的字符串
boolean anyStartsWithA = strings.stream()
.anyMatch(s -> s.startsWith("a"));
// 是否所有字符串长度都大于 2
boolean allLengthGreaterThan2 = strings.stream()
.allMatch(s -> s.length() > 2);
// 是否所有字符串都不为空
boolean noneEmpty = strings.stream()
.noneMatch(String::isEmpty);
System.out.println("Any starts with 'a': " + anyStartsWithA);
System.out.println("All length greater than 2: " + allLengthGreaterThan2);
System.out.println("None empty: " + noneEmpty);
}
}
6. findFirst
、findAny
这两个方法用于从流中查找元素,返回一个 Optional
对象。
功能
findFirst
:返回流中的第一个元素,适用于有序流。findAny
:返回流中的任意一个元素,适用于并行流。
使用场景
- 查找流中的某个元素。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class FindExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "def", "ghi");
// 查找第一个元素
Optional<String> first = strings.stream()
.findFirst();
// 查找任意一个元素
Optional<String> any = strings.stream()
.findAny();
first.ifPresent(s -> System.out.println("First: " + s));
any.ifPresent(s -> System.out.println("Any: " + s));
}
}
以下是一些额外的终止操作及其详细说明和示例代码:
7. toArray
toArray
是用于将流中的元素收集到一个数组中。
功能
- 将流中的元素转换为一个数组。
使用场景
- 当需要将流转换为数组进行进一步处理时使用。
示例代码
import java.util.Arrays;
import java.util.List;
public class ToArrayExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "def", "ghi");
// 将流转换为数组
String[] stringArray = strings.stream()
.toArray(String[]::new);
System.out.println("Array: " + Arrays.toString(stringArray));
}
}
8. min
和 max
min
和 max
是用于根据给定的比较器找出流中的最小值和最大值,返回一个 Optional
对象。
功能
min
:找出流中的最小值。max
:找出流中的最大值。
使用场景
- 当需要找到集合中的最小或最大元素时使用。
示例代码
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class MinMaxExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(3, 5, 1, 2, 4);
// 找出最小值
Optional<Integer> min = numbers.stream()
.min(Comparator.naturalOrder());
// 找出最大值
Optional<Integer> max = numbers.stream()
.max(Comparator.naturalOrder());
min.ifPresent(value -> System.out.println("Min: " + value));
max.ifPresent(value -> System.out.println("Max: " + value));
}
}
9. summaryStatistics
summaryStatistics
是用于获取流中元素的统计信息,如计数、总和、最小值、最大值和平均值。该操作通常用于数值流。
功能
- 获取流中元素的统计信息。
使用场景
- 当需要一次性获取流中的各种统计数据时使用。
示例代码
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
public class SummaryStatisticsExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(3, 5, 1, 2, 4);
// 获取统计信息
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());
}
}
10. joining
joining
是一个特殊的收集器,它将流中的字符串连接成一个字符串。可以指定分隔符、前缀和后缀。
功能
- 将流中的字符串连接成一个字符串。
使用场景
- 当需要将多个字符串连接成一个时使用。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class JoiningExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "def", "ghi");
// 将字符串连接成一个字符串,使用逗号分隔
String result = strings.stream()
.collect(Collectors.joining(", "));
System.out.println("Joined String: " + result);
}
}
11. groupingBy
groupingBy
是一个特殊的收集器,用于将流中的元素按指定的分类函数进行分组,返回一个 Map
,其中键是分类函数的结果,值是属于该分类的元素列表。
功能
- 将流中的元素按分类函数进行分组。
使用场景
- 当需要按某个标准对流中的元素进行分组时使用。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GroupingByExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 按字符串长度分组
Map<Integer, List<String>> groupedByLength = strings.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("Grouped by Length: " + groupedByLength);
}
}
12. partitioningBy
partitioningBy
是一个特殊的收集器,用于将流中的元素按指定的谓词(返回 boolean
的函数)进行分区,返回一个 Map
,其中键是 boolean
值,值是属于该分区的元素列表。
功能
- 将流中的元素按谓词进行分区。
使用场景
- 当需要按某个条件将流中的元素分成两组时使用。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class PartitioningByExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 按是否为偶数分区
Map<Boolean, List<Integer>> partitionedByEven = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("Partitioned by Even: " + partitionedByEven);
}
}
以上是一些其他常见的 Stream 流终止操作。通过这些操作,可以实现对流中数据的各种处理和转换,生成需要的结果。