java8中stream流的常见操作

介绍 Java 8 中引入的 Stream API 的基本概念、创建方法及常见操作,包括中间操作与终止操作,帮助理解如何高效处理集合数据。

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

一、概述

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

二、Stream流操作步骤概述

  1. 创建Stream:一个数据源(如:集合、数组),获取一个流;

  2. 进行中间操作:一个中间操作链,对数据源的数据进行处理;

  3. 进行终止操作:一个终止操作,执行中间操作链,并产生结果。

三、Stream流的常见创建方法

  1. 通过Collection系列集合提供的顺序流stream()或并行流parallelStream()
 		List<Integer> list = new ArrayList<>();
        // 创建一个顺序流
        Stream<Integer> stream = list.stream();
        // 创建一个并行流
        Stream<Integer> parallelStream = list.parallelStream();
  1. 通过Arrays中的静态方法stream()获取数据流
		Integer[] intArr = new Integer[2];
        Stream<Integer> stream = Arrays.stream(intArr);
  1. 使用Stream中的静态方法:of()、iterate()、generate()
		// of方法
        Stream<Integer> of = Stream.of(1, 2, 3, 4, 5);
        // iterate方法,第一个参数seed种子(初始值),第二个参数定义数据的生成规则
        Stream<Integer> limit = Stream.iterate(1, (x) -> {
            return x + 1;
        }).limit(5);
        // 遍历
        limit.forEach((x) -> {
            System.out.println(x);
        });
        // generate方法
        Stream<Double> stream = Stream.generate(Math::random).limit(5);
        // 遍历
        stream.forEach(System.out::println);
  1. 使用 BufferedReader.lines() 方法,将每行内容转成流
        BufferedReader reader = new BufferedReader(new FileReader("D:\\test.txt"));
        Stream<String> lineStream = reader.lines();

5.使用 Pattern.splitAsStream() 方法,将字符串分隔成流

		Pattern pattern = Pattern.compile(",");
        Stream<String> splitAsStream = pattern.splitAsStream("a,b,c,d");
        splitAsStream.forEach(System.out::println);

四、Stream流的常见中间操作

  1. 筛选与切片

filter:过滤流中的某些元素

		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 过滤出来偶数
        Stream<Integer> filter = stream.filter((x) -> {
            return (x & 1) == 0;
        });
        filter.forEach((x) -> {
            System.out.print(x + "\t");
        });
         // 2    2   8   

limit(n):获取n个元素,n小于0时抛出异常,n大于stream 中的元素个数时,获取全部元素

		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 取前3个元素
        Stream<Integer> stream2= stream.limit(3);
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 2    2   3   

skip(n):跳过n元素,配合limit(n)可实现分页,n小于0时,抛出异常,n等于0时,获取所有元素,n大于stream 中的元素个数时,获取为空。

		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 跳过前3个元素
        Stream<Integer> stream2 = stream.skip(3);
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 7 8

distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 去重
        Stream<Integer> stream2 = stream.distinct();
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        //2 3   7   8   
  1. 映射

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

 		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 将所有元素通过map映射处理成double类型
        Stream<Double> stream2 = stream.map((x) -> {
            return x * 1.0;
        });
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 2.0 2.0 3.0 7.0 8.0

flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

		Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
        // 将所有元素通过map映射处理成double类型
        Stream<Double> stream2 = stream.flatMap((x) -> {
            // 处理成stream流
            Stream<Double> s = Stream.of(x * 1.0);
            return s;
        });
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 2.0 2.0 3.0 7.0 8.0
  1. 排序

sorted():自然排序,流中元素需实现Comparable接口

		Stream<Integer> stream = Stream.of(2, 1, 7, 3, 8);
        // 将元素从小到大排序
        Stream<Integer> stream2 = stream.sorted();
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 1 2 3 7 8

sorted(Comparator com):定制排序,自定义Comparator排序器

		Stream<Integer> stream = Stream.of(2, 1, 7, 3, 8);
        // 自定义排序方式,将元素从大到小排序
        Stream<Integer> stream2 = stream.sorted((a, b) -> {
            return Math.negateExact(a - b);
        });
        stream2.forEach((x) -> {
            System.out.print(x + "\t");
        });
        // 8    7   3   2   1   
  1. 消费
    peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
	static class User {
        public int id;
        public String name;

        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + "]";
        }

    }

    public static void main(String[] args) {
        Stream<User> stream = Arrays.asList(new User[] {new User(), new User()}).stream();
        // 使用peek初始化stream中的元素
        Stream<User> stream2 = stream.peek((x) -> {
            x.id = new Random().nextInt(10);
            x.name = "name" + x.id;
        });
        stream2.forEach((x) -> {
            System.out.println(x);
        });
        // User [id=9, name=name9]
        // User [id=4, name=name4]
    }

五、Stream流的常见终止操作

  1. 匹配、聚合操作

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 判断stream中是否全是偶数
        boolean allMatch = stream.allMatch(x -> (x & 1) == 0);
        System.out.println(allMatch); // false

noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 判断stream中是否全是大于0的数
        boolean noneMatch = stream.noneMatch(x -> x < 0);
        System.out.println(noneMatch);// true

anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 判断stream中是否有大于6的数
        boolean anyMatch = stream.anyMatch(x -> x > 6);
        System.out.println(anyMatch);// true

findFirst:返回流中第一个元素

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中第一个元素
        Optional<Integer> findFirst = stream.findFirst();
        System.out.println(findFirst.get());// 2

findAny:返回流中的任意元素

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中的任意元素
        Optional<Integer> findAny = stream.findAny();
        System.out.println(findAny.get());// 2

count:返回流中元素的总个数

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中元素的总个数
        long count = stream.count();
        System.out.println(count);// 5

max:返回流中元素最大值

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中元素最大值
        Integer max = stream.max(Integer::compare).get();
        System.out.println(max);// 7

min:返回流中元素最小值

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 返回流中元素最小值
        Integer min = stream.min(Integer::compare).get();
        System.out.println(min);// 2
  1. 规约操作

Optional<T> reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。

Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 实现数字累加
        Optional<Integer> reduce = stream.reduce((a, b) -> {
            return a + b;
        });
        System.out.println(reduce.get()); // 24

T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。

        Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 实现数字累加,在原基础上加100
        Integer reduce = stream.reduce(100, (a, b) -> {
            return a + b;
        });
        System.out.println(reduce); // 124

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner):在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

顺序流第三个参数不起作用:

	Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
        // 实现数字累加,在原基础上加100
        // 顺序流
        Integer reduce = stream.reduce(100, (a, b) -> {
            System.out.println("---first---");
            return a + b;
        }, (a, b) -> {
            System.out.println("---second---");
            return a + b;
        });
        System.out.println(reduce); // 124
        // ---first---
        // ---first---
        // ---first---
        // ---first---
        // ---first---
        // 124

并行流,相当于把第一种和第二种方法都用到了:

        Stream<Integer> parallelStream = Arrays.asList(new Integer[] {2, 4, 5, 6, 7}).parallelStream();
        // 实现数字累加,在原基础上加100
        // 顺序流
        Integer reduce = parallelStream.reduce(100, (a, b) -> {
            System.out.println("---first---" + (a + b));
            return a + b;
        }, (a, b) -> {
            System.out.println("---second---" + (a + b));
            return a + b;
        });
        System.out.println(reduce); // 124
        // ---first---105
        // ---first---107
        // ---first---106
        // ---second---213
        // ---second---318
        // ---first---104
        // ---first---102
        // ---second---206
        // ---second---524
        // 524
  1. 收集操作

将流转换为其他形式,接收一个Collertor接口的实现。

collect:接收一个Collector实例,将流中元素收集成另外一个数据结构,使用Collector 工具库:Collectors。

  • List:把流中所有元素收集到List中,使用.collect(Collectors.toList());
		Stream<Integer> stream = Stream.of(1, 5, 2, 3, 4);
        List<Integer> list = stream.collect(Collectors.toList());
        list.forEach(System.out::println);
  • Set:把流中所有元素收集到Set中,删除重复项,使用.collect(Collectors.toSet());
		Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Set<Integer> set = stream.collect(Collectors.toSet());
        set.forEach(System.out::println);
  • Map:把流中所有元素收集到Map中,当出现相同的key时会抛异常,使用 .collect(Collectors.toMap());
        Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Map<String, Integer> map = stream.collect(Collectors.toMap(
            // 定义key
            (x) -> {
                return "m" + x;
            },
            // 定义value
            (x) -> {
                return x;
            },
            // 当存在key冲突时,解决方法,这里默认使用第一个key对应的value值
            (a, b) -> {
                return a;
            }));
        map.forEach((key, value) -> {
            System.out.println(key + "=" + value);
        });
  • 使用collect方法求流中共有几条数据,使用.collect(Collectors.counting())
        Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Long count = stream.collect(Collectors.counting());
        System.out.println(count); // 5
  • 使用collect方法求平均数,使用.collect(Collectors.averagingInt();
        Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Double av = stream.collect(Collectors.averagingInt((x) -> {
            return x;
        }));
        System.out.println(av); // 2.4
  • 使用collect方法求某个变量的总和,使用.collect(Collectors.summingInt());
        Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Integer sum = stream.collect(Collectors.summingInt((x) -> {
            return x;
        }));
        System.out.println(sum); // 12
  • 使用collect方法且某个变量中值的最大值,使用.collect(Collectors.maxBy());
        Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
        Optional<Integer> max = stream.collect(Collectors.maxBy(Integer::compare));
        System.out.println(max.get()); // 4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder-文小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值