Java Stream流详解:从入门到精通

目录

引言:为什么需要Stream流

一、Stream流基础

1.1 什么是Stream流

1.2 Stream流与集合的区别

1.3 Stream流的使用步骤

二、创建Stream流

2.1 从不同数据源创建Stream

2.1.1 从单列集合创建Stream(最常用)

2.1.2 从双列集合创建Stream

2.1.3 从数组创建Stream

2.1.4 从零散数据创建Stream

三、Stream流的中间操作

3.1 筛选和切片操作

3.2 映射操作

3.3 排序操作

3.4 查看操作(peek)

3.5 注意事项

四、Stream流的终结方法

4.1 遍历操作

4.2 计数操作

4.3 收集操作

4.4 匹配操作

4.5 查找操作

4.6 归约操作(reduce)

4.7 最大值和最小值

五、collect收集器详解

5.1 收集到List、Set、Map

5.2 收集并分组

5.3 其他常用收集操作

六、Stream流的实际应用案例

6.1 案例一:过滤和转换

6.2 案例二:统计和聚合

6.3 案例三:排序和限制

七、并行流

7.1 创建并行流

7.2 并行流示例

7.3 并行流注意事项

八、常见问题与注意事项

8.1 Stream的一次性使用

8.2 延迟执行特性

8.3 避免副作用

8.4 调试Stream流

8.5 性能考虑

九、面试常见问题

9.1 Stream的特点是什么?

9.2 Stream流、Collection和数组的区别?

9.3 介绍一下Stream流的工作流程

9.4 什么是中间操作和终端操作?

9.5 并行流和顺序流有什么区别?

十、总结


引言:为什么需要Stream流

想象一下,你需要从一堆水果中挑选出所有红色的、重量超过200克的苹果,并按重量排序取前三个。传统方式需要你写多个循环和条件判断,而使用Stream流,你可以这样做:

List<Apple> result = apples.stream()
                          .filter(a -> a.getColor().equals("红色"))
                          .filter(a -> a.getWeight() > 200)
                          .sorted((a1, a2) -> a1.getWeight() - a2.getWeight())
                          .limit(3)
                          .collect(Collectors.toList());

Stream流就像一条传送带,数据放上去后可以经过多个处理站,最终得到你想要的结果,让代码更简洁、可读性更强。

一、Stream流基础

1.1 什么是Stream流

Stream流是Java 8引入的处理集合数据的API,它让我们以声明式(描述要做什么,而不是怎么做)的方式处理数据。

核心特点

  • 声明式编程:更关注做什么,而不是怎么做
  • 支持链式操作:可以像搭积木一样组合多个操作
  • 支持并行处理:轻松切换到并行模式,提高性能
  • 延迟执行:在最后一步(终端操作)才会真正执行

1.2 Stream流与集合的区别

简单对比:

  • 集合就像一个仓库,专注于存储和访问数据
  • Stream就像一条流水线,专注于对数据进行操作
特性 集合 Collection Stream流
存储方式 存储元素 不存储元素
操作时机 立即执行 延迟执行(惰性)
遍历次数 可多次遍历 只能遍历一次
关注点 数据 计算

1.3 Stream流的使用步骤

Stream流的使用分为三个步骤:

  1. 创建Stream流:获取一个数据流
  2. 使用中间方法:对流中的数据进行操作
  3. 使用终结方法:对流水线上的数据进行最终操作
// 一个完整的Stream操作示例
List<String> names = Arrays.asList("小明", "小红", "小刚", "小华");

List<String> filteredNames = names.stream()           // 1.创建Stream流
                                 .filter(name -> name.length() > 1)  // 2.中间操作
                                 .collect(Collectors.toList());       // 3.终端操作

二、创建Stream流

2.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接口中的静态方法
2.1.1 从单列集合创建Stream(最常用)
// 1.单列集合获取Stream流
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d", "e");
// 获取一条流水线,并把集合中的数据放到流水线上
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));
2.1.2 从双列集合创建Stream

双列集合没有直接获取Stream流的方法,但可以间接获取:

// 1.创建双列集合
HashMap<String, Integer> hm = new HashMap<>();
// 2.添加数据
hm.put("红楼梦", 111);
hm.put("西游记", 222);
hm.put("水浒传", 333);
hm.put("三国演义", 444);

// 3.第一种获取stream流的方式:通过键
hm.keySet().stream().forEach(s -> System.out.println(s));

// 4.第二种获取stream流的方式:通过值
hm.values().stream().forEach(i -> System.out.println(i));

// 5.第三种获取stream流的方式:通过键值对
hm.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue()));
2.1.3 从数组创建Stream
// 1.创建数组
int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
String[] arr2 = {"a", "b", "c"};

// 2.获取stream流
Arrays.stream(arr1).forEach(s -> System.out.println(s));
System.out.println("========================");
Arrays.stream(arr2).forEach(s -> System.out.println(s));
2.1.4 从零散数据创建Stream
// 一堆零散数据    public static<T> Stream<T> of(T... values)    Stream接口中的静态方法
Stream.of(1, 2, 3, 4, 5).forEach(s -> System.out.println(s));
Stream.of("a", "b", "c", "d", "e").forEach(s -> System.out.println(s));

三、Stream流的中间操作

中间操作会返回一个新的Stream,可以链式调用。中间操作不会立即执行,而是等到终端操作时才一起执行。

Stream的中间方法包括:

名称 说明
filter(Predicate<?> predicate) 过滤
limit(long maxSize) 获取前几个元素
skip(long n) 跳过前几个元素
distinct() 元素去重(依赖hashCode和equals方法)
concat(Stream a, Stream b) 合并a和b两个流为一个流
map(Function<T, R> mapper) 转换流中的数据类型

3.1 筛选和切片操作

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 5);

// filter: 过滤元素
List<Integer> greaterThan3 = numbers.stream()
                                  .filter(n -> n > 3)
                                  .collect(Collectors.toList());
System.out.println("大于3的数: " + greaterThan3);  // [4, 5]

// distinct: 去除重复
List<Integer> uniqueNumbers = numbers.stream()
                                   .distinct()
                                   .collect(Collectors.toList());
System.out.println("去重后: " + uniqueNumbers);  // [1, 2, 3, 4, 5]

// limit: 限制数量
List<Integer> firstThree = numbers.stream()
                                .limit(3)
                                .collect(Collectors.toList());
System.out.println("前三个数: " + firstThree);  // [1, 2, 2]

// skip: 跳过元素
List<Integer> skipFirst2 = numbers.stream()
                                .skip(2)
                                .collect(Collectors.toList());
System.out.println("跳过前两个: " + skipFirst2);  // [2, 3, 4, 5]

// 组合使用
List<Integer> result = numbers.stream()
                            .distinct() // 先去重
                            .skip(1)    // 跳过第一个
                            .limit(3)   // 取3个
                            .collect(Collectors.toList());
System.out.println("去重后跳过第一个,再取三个: " + result); // [2, 3, 4]

3.2 映射操作

List<String> words = Arrays.asList("Java", "Stream", "API");

// map: 转换元素
List<Integer> wordLengths = words.stream()
                               .map(word -> word.length())  // 获取每个单词的长度
                               .collect(Collectors.toList());
System.out.println("单词长度: " + wordLengths);  // [4, 6, 3]

// map的另一个示例:转换大写
List<String> upperCaseWords = words.stream()
                                 .map(word -> word.toUpperCase())
                                 .collect(Collectors.toList());
System.out.println("转换为大写: " + upperCaseWords);  // [JAVA, STREAM, API]

// flatMap: 扁平化处理(将多个流合并为一个流)
List<List<Integer>> nestedList = Arrays.asList(
    Arrays.asList(1, 2), 
    Arrays.asList(3, 4, 5)
);
List<Integer> flatList = nestedList.stream()
                                 .flatMap(list -> list.stream())  // 将每个内部List转为Stream然后合并
                                 .collect(Collectors.toList());
System.out.println("扁平化后: " + flatList);  // [1, 2, 3, 4, 5]

// fl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值