Java 8新特性Stream()流

简介

Java 8 引入的 Stream API 是 Java 平台的一个重大改进,它提供了一种高效且表达力强的方式来处理数据集合(如 List、Set)。Stream API 可以让你以声明方式处理数据集合(专注于你要做什么,而不是怎么做),并且可以利用多核处理器的优势进行并行计算。Stream API 是 Java 8 引入的一个功能强大的工具,它提供了一种简洁而高效的方式来处理集合数据。通过使用 Stream,我们可以对集合进行过滤、映射、排序、聚合等操作,而无需编写繁琐的循环和条件语句。Stream 的操作可以串行执行,也可以并行执行,从而提高处理大量数据的效率。

主要特性和优点

  1. 函数式编程支持:Stream API 充分利用了 Java 8 的 Lambda 表达式和函数式编程概念。
  2. 声明式数据处理:通过 Stream API,你可以以接近自然语言的方式表达数据处理逻辑,代码更加清晰、简洁。
  3. 并行处理能力:Stream API 提供了自动的并行处理能力,可以简化并行编程的复杂性,可以自动利用多核处理器,提高处理大量数据的效率。
  4. 延迟执行:Stream API 的操作是延迟执行的,这意味着它们不会立即执行,而是等到需要结果时才执行。
  5. 无副作用:Stream API 的操作应该是无副作用的,即它们不会修改数据源。
  6. 易于集成和扩展:Stream API 可以与 Java 集合框架无缝集成,并且可以很容易地通过自定义的 Spliterator 进行扩展。

基本操作

Stream API 中的操作分为两类:中间操作(Intermediate Operations)和终端操作(Terminal Operations)。

1、中间操作:返回 Stream 本身,因此可以链式调用。中间操作是“惰性”的,即在调用终端操作之前,它们不会执行任何处理。常见的中间操作包括 filter(), map(), sorted(), limit(), skip() 等。

中间操作包括有状态和无状态操作两种:

  • 有状态操作:操作需要维护状态来正确执行,每个元素的处理可能依赖于其他元素的状态或上下文。
    例如,sorted和distinct操作,是需要维护一个状态,一个是记录位置,一个是记录是否出现过。

  • 无状态操作:每个元素的处理都是独立的,不依赖于其他元素的状态。
    例如,filter、map和flatMap,只是根据输入元素生成输出元素,而不会受到其他元素的影响。

操作类型 操作方法 描述
中间操作 filter(Predicate<? super T> predicate) 过滤流中的元素,只保留满足谓词条件的元素
map(Function<? super T, ? extends R> mapper) 将流中的每个元素映射成另一种形式
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 将流中的每个元素都转换成另一个流,然后将所有流连接成一个流
distinct() 去除流中的重复元素(基于元素的 equals()hashCode() 方法)
sorted() 对流中的元素进行自然排序(需要元素实现了 Comparable 接口)
sorted(Comparator<? super T> comparator) 使用自定义的比较器对流中的元素进行排序
peek(Consumer<? super T> action) 对流中的每个元素执行操作,但不影响流本身,主要用于调试
limit(long maxSize) 限制流的元素个数
skip(long n) 跳过流的前n个元素

2、终端操作:返回一个结果或副作用,例如执行一个操作或返回一个非 Stream 的值。终端操作会触发 Stream 管道中所有中间操作的执行。常见的终端操作包括 forEach(), collect(), reduce(), findFirst(), min(), max() 等。

终止操作包括短路操作和非短路操作两种:

  • 短路操作:处理元素时,满足某个条件就立即返回结果,无需处理所有元素。
    例如,findFirst、findAny、anyMatch和allMatch
  • 非短路操作:指必须处理所有元素才能得到最终结果;
    例如,forEach、reduce和collect
操作类型 操作方法 描述
终止操作 forEach(Consumer<? super T> action) 遍历流中的每个元素并执行操作
toArray() 将流中的元素收集到一个数组中(注意:这通常需要一个显式的类型参数,如 toArray(String[]::new)
reduce(BinaryOperator<T> accumulator) 归约操作,将流中的元素组合起来,得到一个值(需要元素之间有明确的组合方式)
reduce(T identity, BinaryOperator<T> accumulator) 带初始值的归约操作
collect(Collector<? super T, A, R> collector) 收集操作,将流中的元素收集到一个集合中,通常与 Collectors 类一起使用
min(Comparator<? super T> comparator) 找出流中的最小元素(根据提供的比较器)
max(Comparator<? super T> comparator) 找出流中的最大元素(根据提供的比较器)
count() 计算流中元素的个数
anyMatch(Predicate<? super T> predicate) 检查流中是否存在至少一个元素满足条件
allMatch(Predicate<? super T> predicate) 检查流中的所有元素是否都满足条件
noneMatch(Predicate<? super T> predicate) 检查流中是否不存在任何元素满足条件
findFirst() 查找流中的第一个元素(返回一个包含单个元素的 Optional
findAny() 查找流中的任意一个元素(在并行流中可能不返回第一个元素)

官网: Stream()官网

一、操作

1、获取流
1.1 从集合(Collection)中获取
	@Test
    void list() {
   
   
        // 创建一个List集合
        List<String> names = Arrays.asList("John", "Alice", "Bob", "David");

        // 获取顺序流
        Stream<String> stream = names.stream();

        // 使用流进行操作(这里只是示例,没有实际的操作)
         stream.forEach(System.out::println);

        // 获取并行流
        Stream<String> parallelStream = names.parallelStream();

        // 使用并行流进行操作(这里只是示例,没有实际的操作)
        parallelStream.forEach(System.out::println);

        Set<Integer> set = new HashSet<Integer>() {
   
   {
   
   
            add(1);
            add(2);
            add(3);
        }};
        // 通过Set获取
        Stream<Integer> stream2 = set.stream();
        stream2.forEach(System.out::println);
        Map<String, String> map = new HashMap<>();
        map.put("map1","test1");
        map.put("map2","test2");
        // 通过Map.entrySet获取
        Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
        stream3.forEach(System.out::println);
        // 通过Map.keySet获取
        Stream<String> stream4 = map.keySet().stream();
        stream4.forEach(System.out::println);
        // 通过Map.values获取
        Stream<String> stream5 = map.values().stream();
        stream5.forEach(System.out::println);
    }
1.2 从数组中获取
	@Test
    void array(){
   
   
        // 对象数组
        String[] strings = {
   
   "Hello", "World", "Java", "Stream"};
        Stream<String> stringStream = Arrays.stream(strings);

        // 基本类型数组(以int为例)
        int[] numbers = {
   
   1, 2, 3, 4, 5};
        Stream<Integer> intStream = Arrays.stream(numbers).boxed(); // 基本类型流需要装箱

        // 使用流进行操作(这里只是示例,没有实际的操作)
         stringStream.forEach(System.out::println);
         intStream.forEach(System.out::println);
    }
1.3 从其他数据源获取流
    @Test
    void stream(){
   
   
        // 使用Stream.of()从多个元素中创建流
        Stream<String> stringStream = Stream.of("Apple", "Banana", "Cherry");

        // 使用Stream.iterate()创建无限流(这里需要提供一个终止条件来限制流的大小)
        Stream<Integer> infiniteIntStream = Stream.iterate(1, n -> n + 1).limit(5); // 生成1到5的流

        // 使用Stream.generate()创建无限流(基于提供的Supplier)
        Stream<Double> randomStream = Stream.generate(Math::random).limit(10); // 生成10个随机数
        
         stringStream.forEach(System.out::println);
         infiniteIntStream.forEach(System.out::println);
         randomStream.forEach(System.out::println);
    }
1.4 从文件中创建流
    @Test
    void file(){
   
   
        // Paths.get("example.txt") 为当前工作目录下名为 example.txt 的文件,没有创建会报错,也可以写绝对路径
        try (Stream<String
<think>嗯,用户想学习Java 8Stream,该怎么教他们呢?首先得了解他们的背景。可能他们已经有一些Java基础,但没接触过Stream。需要从基础开始,逐步深入。 首先,得介绍Stream是什么。用户可能听说过Stream处理集合更方便,但具体概念还不清楚。要解释Stream和集合的区别,比如元素序列、源数据、函数式操作这些点。可能还要提到中间操作和终端操作的区别,比如filter是中间操作,collect是终端操作,这样他们不会混淆。 然后,需要举一些简单的例子,比如创建Stream的方式。从集合转换是最常见的,可以用stream()方法。数组的话用Arrays.stream,或者直接用Stream.of。例子要简单明了,比如用List转Stream,这样用户能马上上手。 接下来是常用操作。filter、map、sorted这些中间操作,还有collect、forEach这些终端操作。每个操作都需要例子说明。比如用filter过滤字符串长度,用map转换成大写,sorted排序。例子代码要简洁,展示出链式调用的优势,对比传统循环,突出Stream的简洁性。 然后,可能用户会想知道更多高级操作,比如flatMap处理嵌套集合,reduce进行归约,还有统计操作如sum、average。这时候需要例子展示如何扁平化,比如把多个列表合并,或者计算总和。这些例子能帮助用户理解更复杂的场景。 并行也是一个重点,毕竟Stream的优势之一就是并行处理。需要解释parallelStream的使用,但也要提醒线程安全的问题,比如在reduce操作中累加器要符合规范,避免并发问题。例子用并行计算总和,对比串行,说明性能提升的可能性。 接下来,学习资源推荐。官方文档是必须的,但可能有些用户觉得枯燥,所以推荐一些书籍,比如《Java 8实战》,还有在线教程如Baeldung,这些资源比较友好。另外,实践项目很重要,可以建议他们用Stream重构旧代码,或者处理数据,比如统计日志信息,这样能巩固所学。 最后,注意事项部分要强调Stream的一次性消费,避免重复使用;区分中间和终端操作,防止遗漏终端操作导致不执行;性能方面,小数据可能传统循环更快,大数据用并行。这些注意事项能帮助用户避免常见错误。 整个思考过程中,要确保结构清晰,从概念到操作,再到高级特性和资源,逐步引导用户。例子要具体,避免太抽象的解释,让用户能动手实践。同时,提醒他们注意事项,避免踩坑。可能用户需要对比传统方式和Stream的区别,所以在例子中适当比较,突出Stream的优点。还要考虑用户可能遇到的问题,比如为什么终端操作没执行,或者并行的线程安全,提前给出提示。</think>要系统学习Java 8Stream,可以按以下步骤逐步掌握: --- ### 一、理解核心概念 1. **什么是Stream**: - 不是数据结构,而是对数据源(集合、数组等)进行函数式操作的水线 - 特点:不存储数据、不修改源数据、延迟执行 2. **与集合的区别**: $$集合 = 存储 + 操作数据 \quad vs \quad Stream = 声明式操作$$ --- ### 二、掌握基础操作 #### 1. 创建Stream ```java // 从集合创建 List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream(); // 从数组创建 String[] array = {"a", "b"}; Stream<String> stream = Arrays.stream(array); // 直接创建 Stream<String> stream = Stream.of("a", "b"); ``` #### 2. 常用中间操作 ```java List<String> result = list.stream() .filter(s -> s.length() > 3) // 过滤 .map(String::toUpperCase) // 转换 .sorted() // 排序 .distinct() // 去重 .skip(1) // 跳过元素 .limit(5) // 限制数量 .collect(Collectors.toList()); // 终端操作 ``` --- ### 三、掌握进阶特性 #### 1. 并行 ```java long count = list.parallelStream() // 自动并行处理 .filter(s -> s.contains("a")) .count(); ``` #### 2. 数值(避免装箱) ```java IntStream.rangeClosed(1, 100) // 生成1-100的int .sum(); ``` #### 3. 高级收集器 ```java Map<Integer, List<String>> groupByLength = list.stream() .collect(Collectors.groupingBy(String::length)); String joined = list.stream() .collect(Collectors.joining(", ")); ``` --- ### 四、典型应用场景 1. **数据过滤**: ```java List<User> adults = users.stream() .filter(u -> u.getAge() >= 18) .collect(Collectors.toList()); ``` 2. **数据转换**: ```java List<String> emails = users.stream() .map(User::getEmail) .collect(Collectors.toList()); ``` 3. **统计计算**: ```java Double average = users.stream() .collect(Collectors.averagingInt(User::getAge)); ``` --- ### 五、学习资源推荐 1. **官方文档**: - [Java 8 Stream API](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) 2. **实战书籍**: - 《Java 8实战》(Java 8 in Action) 3. **在线教程**: - Baeldung的[Java Stream指南](https://www.baeldung.com/java-8-streams) - 菜鸟教程的[Java 8新特性](https://www.runoob.com/java/java8-new-features.html) --- ### 六、练习建议 1. **重构现有代码**:将传统for循环改写成Stream操作 2. **解决实际问题**: - 统计日志文件中不同错误类型的出现次数 - 处理嵌套集合(如`List<List<Integer>>`) 3. **参与编程挑战**:在LeetCode等平台用Stream解题 --- ### 七、注意事项 1. **只能消费一次**:终端操作后即关闭 2. **区分中间/终端操作**:缺少终端操作时中间操作不会执行 3. **避免副作用**:不要在lambda表达式中修改外部变量 4. **性能权衡**:小数据集可能传统循环更快,大数据集用并行更高效 通过理论+实践结合,约20小时系统学习即可掌握核心用法。建议从简单案例入手,逐步尝试复杂场景的式处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值