在 Java 编程领域,处理集合和数组是日常开发中极为常见的任务。Java 8 引入的 Stream 流,为我们提供了一种更为强大、高效且简洁的方式来操作数据集合,显著提升了 Java 在数据处理方面的能力,成为现代 Java 编程中不可或缺的一部分。
Stream 流的作用
Stream 流的核心优势在于它整合了 Lambda 表达式,从而极大地简化了对集合和数组的操作。通过 Stream 流,我们可以将复杂的数据处理逻辑以更直观、更紧凑的方式表达出来,避免了传统迭代方式中繁琐的循环和条件判断,使代码的可读性和可维护性得到显著提升。
Stream 流的使用步骤
1. 先得到一条 Stream 流(流水线),并把数据放上去
获取 Stream 流是使用它进行数据处理的第一步,不同的数据结构有不同的获取方式:
| 获取方式 | 方法名 | 说明 |
|---|---|---|
| 单列集合 | default Stream<E> stream() | Collection 中的默认方法 |
| 双列集合 | 无 | 无法直接使用 Stream 流 |
| 数组 | public static<T> Stream<T> stream(T[] array) | Arrays 工具类中的静态方法 |
| 一堆零散数据 | public static<T> Stream<T> of(T...values) | Stream 接口中的静态方法 |
1.1 单列集合获取 Stream 流
// 1. 单列集合获取Stream流
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
// 获取一条流水线,并把集合中的数据放到流水线上
Stream<String> stream1 = list.stream();
// 使用终结方法打印一下流水线上的所有数据
stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
// s: 依次表示流水线的每一个数据
System.out.println(s);
}
});
// 也可以使用Lambda表达式简化代码
list.stream().forEach(s -> System.out.println(s));
1.2 双列集合获取 Stream 流
双列集合(如 Map)不能直接使用 Stream 流,因为 Stream 流主要针对元素序列,而双列集合是键值对形式。不过,我们可以通过keySet().stream()获取键的 Stream 流,或通过entrySet().stream()获取键值对 Entry 对象的 Stream 流。
public static void main(String[] args) {
// 1. 创建双列集合
HashMap<String, Integer> hm = new HashMap<>();
// 添加数据
hm.put("aaa", 111);
hm.put("bbb", 222);
hm.put("ccc", 333);
// 通过keySet().stream()获取键的Stream流
hm.keySet().stream().forEach(s -> System.out.println(s));
// 通过entrySet().stream()获取键值对Entry对象的Stream流
hm.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + " : " + entry.getValue()));
}
1.3 数组获取 Stream 流
public static void main(String[] args) {
// 1. 创建数组
int[] arr1 = {1, 2, 3, 4, 5, 6};
String[] arr2 = {"a", "b", "c"};
// 2. 获取stream流
Arrays.stream(arr1).forEach(s -> System.out.println(s));
Arrays.stream(arr2).forEach(s -> System.out.println(s));
// 注意
// stream接口中静态方法of的细节
// 方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组
// 但是数组必须是引用数据类型的,如果传递基本数据类型,是会把整个数组当作一个元素,放到stream当中
Stream.of(arr1).forEach(s -> System.out.println(s));// [I@19dfb72a
// 这里输出[I@19dfb72a是因为Stream.of方法将基本数据类型数组当作一个元素,而数组默认的toString方法返回这种形式
}
1.4 一堆零散数据获取 Stream 流
public static void main(String[] args) {
// 一堆零散数据
Stream.of(1, 2, 3, 45).forEach(s -> System.out.println(s));
Stream.of("a", 1, 2, 3, 4, 'b').forEach(s -> System.out.println(s));
}
2. Stream 流的中间方法
Stream 流的中间方法用于对数据进行各种转换和过滤操作,它们会返回一个新的 Stream 流,这使得我们可以进行链式调用,构建复杂的数据处理逻辑。
| 名称 | 说明 |
|---|---|
| Stream<T> filter(Predicate<? super T> predicate) | 过滤,保留满足条件的元素 |
| Stream<T> limit(long maxSize) | 获取前几个元素 |
| Stream<T> skip(long n) | 跳过前几个元素 |
| Stream<T> distinct() | 元素去重,依赖(hashCode 和 equals 方法) |
| static<T> Stream<T>concat(Stream a,Stream b) | 合并 a 和 b 两个流为一个流 |
| Stream<R> map(Function<T,R> mapper) | 转换流中的数据类型 |
2.1 filter 方法示例
假设我们有一个整数集合,要过滤出偶数:
// 假设我们有一个整数集合,要过滤出偶数
ArrayList<Integer> numList = new ArrayList<>();
Collections.addAll(numList, 1, 2, 3, 4, 5, 6);
Stream<Integer> filteredStream = numList.stream().filter(n -> n % 2 == 0);
filteredStream.forEach(System.out::println);
2.2 map 方法示例
比如将字符串类型的数字转换为BigDecimal类型:
ArrayList<String> numStrList = new ArrayList<>();
Collections.addAll(numStrList, "1.23", "4.56", "7.89");
Stream<BigDecimal> bigDecimalStream = numStrList.stream().map(BigDecimal::new);
bigDecimalStream.forEach(System.out::println);
2.3 注意事项
- 中间方法返回新的 Stream 流,原来的 Stream 流只能使用一次,建议使用链式编程,以提高代码的可读性和简洁性。
- 修改 Stream 流中的数据,不会影响原来集合或者数组中的数据,Stream 流的操作是基于原数据的一种映射和转换,不会直接修改源数据。
3. Stream 流的终结方法
终结方法是 Stream 流操作的最后一步,调用终结方法后,Stream 流就会被消费,不能再进行后续的 Stream 操作。
| 名称 | 说明 |
|---|---|
| void forEach(Consumer action) | 遍历 |
| long count() | 统计 |
| toArray() | 收集流中的数据,放到数组中 |
| collect(Collector collector) | 收集流中的数据,放到集合中 |
3.1 collect 方法收集到 Map 集合示例
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三-男-15", "周周-女-14", "张强-男-20", "李四-男-15", "王五-男-45");
// 收集Map集合当中
// 我要把所有的男性收集起来
// 键:姓名 值:年龄
Map<String, Integer> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])));
System.out.println(map);
// 使用Collectors.toMap时,如果键冲突会抛出IllegalStateException异常
// 可以使用Collectors.toMap(keyMapper, valueMapper, (oldValue, newValue) -> newValue)
// 这种形式来处理键冲突,保留新值
Map<String, Integer> mapWithConflictHandling = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2]),
(oldValue, newValue) -> newValue));
System.out.println(mapWithConflictHandling);
}
3.2 toArray 方法示例
ArrayList<String> strList = new ArrayList<>();
Collections.addAll(strList, "a", "b", "c");
Object[] array = strList.stream().toArray();
for (Object obj : array) {
System.out.println(obj);
}
Stream 流为 Java 开发者提供了一种强大而灵活的数据处理方式。通过合理运用 Stream 流的获取方式、中间方法和终结方法,我们能够以更高效、更优雅的代码实现复杂的数据处理需求,提升程序的性能和可维护性。无论是日常的业务开发,还是处理大规模数据的场景,Stream 流都能成为我们编程路上的得力助手。
2114



被折叠的 条评论
为什么被折叠?



