Stream流

本文详细介绍了Java Stream流的概念、用途、构造方法以及常用操作,包括无状态和有状态的中间操作,如filter、map、distinct、sorted等,以及终止操作如forEach、collect、limit等。Stream流提供了一种高效且易读的处理数据的方式,结合Lambda表达式,大大提升了Java编程的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Stream流

1. 流的由来

  • 在数据来源多样化的今天,在做数据统计和处理的时候,大多是以底层数据库或其他数据来源的数据为基础进行更上层的统计和处理。
  • 在Java 的集合 API 中,辅助集合操作的方法太少
  • 对数据的筛选、聚合操作等都需要程序员遍历集合进行,效率低下,笨拙
  • Stream流是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常遍历、高效的聚合操作或者大数据的批量操作。
  • 结合Lambda表达式,提高编程效率和程序的可读性。

2. Stream流

  • Stream流是一个来自数据源的元素队列并支持聚合操作
    • 元素是特定类型的对象,形成一个队列。
    • 数据源:流的来源,可以是集合、数组、I/O等
    • 聚合操作: 类似SQL 语句一样的操作,比如filter、map、reduce、find、match、sorted等
    • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。
    • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

3. 流的构成

  • 流处理数据的过程类似工厂的流水线。

  • 在这里插入图片描述

    • 其中最主要的是ReferencePipeline,继承自AbstractPipeline,AbstractPipeline实现了BaseStream接口并实现了它的方法。
    • ReferencePipeline内部定义了三个静态内部类:Head, StatelessOp, StatefulOp.
    • Head:代表的是流的源头,刚构建的时候流的一些属性被包含在这个对象。比如这个集合的元素,毕竟流的存在还是为了对一组元素的操作。
    • StatelessOp:代表的是无状态的操作,如方法 map()
    • StatefulOp:代表的是有状态的操作,如方法 sorted()

4. Stream流的构造

  • static Stream of(T… values)

    • 返回其元素为指定值的顺序有序流
    • 示例:Stream stream = Stream.of("a", "b", "c");
  • Arrays工具类 stream() 方法

    • String [] strArray = new String[] {"a", "b", "c"};
      stream = Arrays.stream(strArray);
      
  • Collections

    • List<String> list = Arrays.asList(strArray);
      stream = list.stream();
      
  • 对于基本数据类型,有三种包装类

    • IntStream,LongStream,DoubleStream

5. 常用方法

Stream 常用的流操作包括:

  • 中间操作(Intermediate Operations)

    • 无状态(Stateless)操作:每个数据的处理是独立的,不会影响或依赖之前的数据。如
      filter()flatMap()flatMapToDouble()flatMapToInt()flatMapToLong()map()mapToDouble()mapToInt()mapToLong()peek()unordered()
    • 有状态(Stateful)操作:处理时会记录状态,比如处理了几个。后面元素的处理会依赖前面记录的状态,或者拿到所有元素才能继续下去。如
      distinct()sorted()sorted(comparator)limit()skip()
  • 终止操作(Terminal Operations)

    • 非短路操作:处理完所有数据才能得到结果。如
      collect()count()forEach()forEachOrdered()max()min()reduce()toArray()等。
    • 短路(short-circuiting)操作:拿到符合预期的结果就会停下来,不一定会处理完所有数据。如
      anyMatch()allMatch()noneMatch()findFirst()findAny() 等。

ForEach

  • void forEach(Consumer<? super T> action)

  • 不说了,遍历用的,

    • /**
       * @Author: Hjx
       * @Date: 2021/8/16 15:30
       */
      public class TestStream {
          public static void main(String[] args) {
              // 获取流
              Stream<String> stream = Stream.of("aaa","bbb","ccc","ddd","eee");
              stream.forEach(string -> System.out.println(string));
          }
      }
      
    • aaa
      bbb
      ccc
      ddd
      eee
      

filter

  • Stream<T> filter(Predicate<? super T> predicate)
    
  • 一般用来筛选元素的,该方法接受一个函数式接口Predicate,可以使用Lambda表达式进行条件的筛选

  • Prodicate接口

    • java.util.stream.Predicate函数式接口,其中唯一的抽象方法
      boolean test(T t),该方法会返回布尔类型值,代表指定的条件是否满足,如果条件满足返回true,那么Stream流的方法filter将集合或者数组其中的元素保留下来,如果条件不满足返回false,那么filter方法会舍弃该元素。
  • /**
     * @Author: Hjx
     * @Date: 2021/8/16 15:30
     */
    public class TestStream {
        public static void main(String[] args) {
            // 获取流
            Stream<String> stream = Stream.of("aaa", "bbb", "ccc", "ddd", "eee");
            stream.filter(string -> !string.equals("aaa"))
                    .forEach(string -> System.out.println(string));
        }
    }
    
  • bbb
    ccc
    ddd
    eee
    

map

  • <R> Stream<R> map(Function<? super T,? extends R> mapper)
    
  • 一般用于将元素转换成另外一种元素类型

  • 该方法接受一个函数式接口Function作为方法参数,可以将当前流中的T数据转换成另一种R类型的数据

  • Function接口

    • 其唯一抽象方法R apply(T t)
    • 可以将一种T类型的数据转换成R类型的数据
  • /**
     * @Author: Hjx
     * @Date: 2021/8/16 15:30
     */
    public class TestStream {
        public static void main(String[] args) {
            // 获取流
            Stream<String> stream = Stream.of("1", "2", "3", "4", "5");
            stream.map(string -> Integer.parseInt(string))
                    .forEach(string -> System.out.println(string.getClass()));
        }
    }
    
  • class java.lang.Integer
    class java.lang.Integer
    class java.lang.Integer
    class java.lang.Integer
    class java.lang.Integer
    

collect

  • <R,A> R collect(Collector<? super T,A,R> collector)
    
  • 终止操作,Stream不调用终止方法,中间的操作不会执行。 作用就是告诉Stream将流中的数据保存到哪里。

  • //1.收集数据到list集合中
    stream.collect(Collectors.toList())
    //2.收集数据到set集合中
    stream.collect(Collectors.toSet())
    

剩下的等用到再补充吧,累了,毁灭吧

2021/8/23 补充

  • 员工类

  • /**
     * @author 16541
     */
    public class Employee {
        /**
         * 员工姓名
         */
        public String name;
        /**
         * 员工年龄
         */
        public Integer age;
        /**
         * 员工薪资
         */
        public Double salary;
    
        public Employee() {
        }
    
        public Employee(String name, Integer age, Double salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Double getSalary() {
            return salary;
        }
    
        public void setSalary(Double salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Employee{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", salary=" + salary +
                    '}';
        }
    }
    

limit

  • Stream<T> limit(long maxSize);

  • 限制获取的数据个数。传入参数是一个long类型数据

  • /**
     * @Author: Hjx
     * @Date: 2021/8/23 14:22
     */
    public class TestMain {
        // 员工集合
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 30, 9999.99),
                new Employee("李四", 20, 7777.77),
                new Employee("王五", 10, 6666.66),
                new Employee("陈六", 27, 4444.44),
                new Employee("田七", 35, 3333.33)
        );
        @Test
        public void test01(){
            employees.stream()
                    .filter(e -> e.getAge() > 25)
                    .limit(2)
                    .forEach(System.out::println);
        }
    }
    运行结果:
    Employee{name='张三', age=30, salary=9999.99}
    Employee{name='陈六', age=27, salary=4444.44}
    
  • /**
     * @Author: Hjx
     * @Date: 2021/8/23 14:22
     */
    public class TestMain {
        // 员工集合
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 30, 9999.99),
                new Employee("李四", 20, 7777.77),
                new Employee("王五", 10, 6666.66),
                new Employee("陈六", 27, 4444.44),
                new Employee("田七", 35, 3333.33)
        );
    
        @Test
        public void test01() {
            employees.stream()
                    .filter(e -> {
                        System.out.println("截断流");
                        return e.getAge() > 25;
                            }
                    )
                    .limit(2)
                    .forEach(System.out::println);
        }
    }
    运行结果:
    截断流
    Employee{name='张三', age=30, salary=9999.99}
    截断流
    截断流
    截断流
    Employee{name='陈六', age=27, salary=4444.44}
    
  • limit属于截断流,就是当获取到达满足的个数时,便不会继续往下遍历了。

skip

  • Stream<T> skip(long n);

  • 跳过指定的数据个数

  • /**
     * @Author: Hjx
     * @Date: 2021/8/23 14:22
     */
    public class TestMain {
        // 员工集合
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 30, 9999.99),
                new Employee("李四", 20, 7777.77),
                new Employee("王五", 10, 6666.66),
                new Employee("陈六", 27, 4444.44),
                new Employee("田七", 35, 3333.33)
        );
        @Test
        public void test02(){
            employees.stream()
                    .filter(e -> e.getSalary() > 4000)
                    .skip(2)
                    .forEach(System.out::println);
        }
    }
    运行结果:
    Employee{name='王五', age=10, salary=6666.66}
    Employee{name='陈六', age=27, salary=4444.44}
    
  • 本来张三和李四也是符合条件薪资大于4000的,但跳过两个,就只剩下王五和陈六了。

distinct

  • Stream<T> distinct();

  • 去除重复元素,依据的原理是 hashCode值 和 equals方法,

  • 重写 employee 的hashCode 方法 和 equals 方法

  • /**
     * @author 16541
     */
    public class Employee {
        /**
         * 员工姓名
         */
        public String name;
        /**
         * 员工年龄
         */
        public Integer age;
        /**
         * 员工薪资
         */
        public Double salary;
    
        public Employee() {
        }
    
        public Employee(String name, Integer age, Double salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Double getSalary() {
            return salary;
        }
    
        public void setSalary(Double salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Employee{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", salary=" + salary +
                    '}';
        }
    
        // 这里判断名字相同即为同一员工
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Employee employee = (Employee) o;
            return name.equals(employee.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }
    
  • /**
     * @Author: Hjx
     * @Date: 2021/8/23 14:22
     */
    public class TestMain {
        // 员工集合
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 30, 9999.99),
                new Employee("李四", 20, 7777.77),
                new Employee("王五", 10, 6666.66),
                new Employee("陈六", 27, 4444.44),
                new Employee("田七", 35, 3333.33),
                new Employee("田七", 35, 3333.33),
                new Employee("田七", 35, 3333.33)
        );
        @Test
        public void test03(){
            employees.stream()
                    .distinct()
                    .forEach(System.out::println);
        }
    }
    运行结果:
    Employee{name='张三', age=30, salary=9999.99}
    Employee{name='李四', age=20, salary=7777.77}
    Employee{name='王五', age=10, salary=6666.66}
    Employee{name='陈六', age=27, salary=4444.44}
    Employee{name='田七', age=35, salary=3333.33}
    

flatMap

  • <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

  • 接收一个函数作为参数,将参数中的每一个元素转换为一个流,然后把所有的流连接起来形成一个流。

  • public class TestMain {
        
        @Test
        public void test04() {
            List<String> list = Arrays.asList("aaa", "bbb", "ccc");
            list.stream()
                    .flatMap(TestMain::flatMapStream)
                    .forEach(System.out::println);
        }
        
        /**
         * 将 String类型 转换为流
         * @param string
         * @return
         */
        public static Stream<Character> flatMapStream(String string){
    
            List<Character> list = new ArrayList<>();
            char[] chars = string.toCharArray();
            for(char ch:chars){
                list.add(ch);
            }
            return list.stream();
        }
    }
    运行结果:
    a
    a
    a
    b
    b
    b
    c
    c
    c
    

sorted

  • Stream<T> sorted();

  • 自然排序,按照字典顺序

  • public class TestMain {
        @Test
        public void test05(){
            List<String> list = Arrays.asList("abc", "aac", "bac", "abb");
            list.stream()
                    .sorted()
                    .forEach(System.out::println);
        }
    }
    运行结果:
    aac
    abb
    abc
    bac
    

sorted(Comparator<? super T> comparator)

  • Stream<T> sorted(Comparator<? super T> comparator);

  • 自定义排序

  • public class TestMain {
    
    	 // 员工集合
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 30, 9999.99),
                new Employee("李四", 20, 7777.77),
                new Employee("王五", 10, 6666.66),
                new Employee("陈六", 27, 4444.44),
                new Employee("田七", 35, 3333.33)
        );
    
        @Test
        public void test05(){
        	// 自定义按照年龄大小排序
            Comparator<Employee> comparator = (x,y) -> x.getAge().compareTo(y.getAge());
            employees.stream()
                    .sorted(comparator)
                    .forEach(System.out::println);
        }
    }
    运行结果:
    Employee{name='王五', age=10, salary=6666.66}
    Employee{name='李四', age=20, salary=7777.77}
    Employee{name='陈六', age=27, salary=4444.44}
    Employee{name='张三', age=30, salary=9999.99}
    Employee{name='田七', age=35, salary=3333.33}
    
<think>好的,用户想要找关于Java Stream的练习题和学习资料。首先,我需要回忆一下相关的知识点,并结合提供的引用内容来整理答案。 首先,用户提供的引用中有四个关于Stream的资料,其中提到了练习题、示例代码以及基础教程。比如引用3提到StreamJava 8的重要特性,允许声明式处理集合,这可能适合作为教程部分的推荐。引用1和引用2给出了具体的练习题,如转换并行、处理绝对值、使用flatMap拆分单词等。引用4则展示了过滤偶数的例子,这些都是很好的练习题素材。 接下来,我需要将这些内容分类整理,可能分为基础概念、练习题推荐和学习资源。同时,用户可能需要教程链接或书籍推荐,但根据引用中没有提供外部链接,可能需要建议官方文档或知名网站。 还要注意用户提到的系统级指令,比如数学公式的格式,但这次问题不涉及数学内容,所以可能不需要处理这部分。另外,生成相关问题的时候,要确保问题与Stream相关,并且基于已有的引用内容。例如,如何优化Stream性能、并行的使用场景、map和filter的区别等。 需要检查是否有遗漏的要点,比如用户可能对Stream的中间操作和终端操作有疑问,或者想了解常见的陷阱。所以在相关问题里可以包括这些方面。 最后,确保回答结构清晰,分点列出,并且每个部分都有对应的引用支持。例如,在练习题部分具体列出每个练习并标注引用来源,这样用户能清楚知道每个例子出自哪里。同时,学习资源部分可以推荐官方文档和书籍,如《Java 8实战》。 总结一下,回答应该包括基础概念、练习题示例、学习资源和相关问题,确保每个部分都有引用支持,并且语言自然畅。</think>### Java Stream学习资源与练习题整理 #### 一、Stream基础概念 StreamJava 8的核心特性之一,允许以声明性方式处理集合数据,支持函数式编程风格。例如: ```java list.stream().filter(n -> n % 2 == 0).forEach(System.out::println); ``` 这段代码通过`filter`筛选偶数并输出[^4]。Stream的核心操作包括中间操作(如`map`、`filter`)和终端操作(如`forEach`、`collect`)[^3]。 --- #### 二、经典练习题推荐(含代码示例) 1. **取元素绝对值** ```java Stream.of(1, -2, -3, 4, -5) .map(Math::abs) .forEach(System.out::println); // 输出1,2,3,4,5 [^2] ``` 2. **拆分单词并合并** ```java List<String> words = Arrays.asList("Hello World", "Java Stream"); words.stream() .flatMap(word -> Arrays.stream(word.split(" "))) .forEach(System.out::println); // 输出拆分后的单词[^1] ``` 3. **过滤奇数保留偶数** ```java List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); list.stream() .filter(n -> n % 2 == 0) .forEach(System.out::println); // 输出2,4,6,8,10 ``` 4. **并行加速计算** ```java list.parallelStream() // 转换为并行 .filter(...); // 利用多核处理器提升性能[^1] ``` --- #### 三、学习资源推荐 1. **官方文档** Oracle Java 8 Stream官方教程:系统介绍Stream API的设计与使用场景。 2. **书籍《Java 8实战》** 详细讲解Stream、Lambda表达式等特性,含大量代码示例。 3. **综合教程与练习题集** - 的入门教程:涵盖Stream操作分类(中间操作、终端操作)和常见用例。 - 中的练习题:覆盖`map`、`filter`、`flatMap`和并行等核心操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值