Java8新特性之Stream

介绍Java8新增的Stream API,涵盖创建Stream的方法、中间操作及终止操作等关键特性。

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

概述

Java8的一个新特性就是增加了Stream API,这东西提供了一个对数据更加便捷的一些操作。

首先就是,相关的类在java.util.stream包里面。其次,流是一种渠道,用来处理对应数据源的的各种元素。

需要注意的是:1、Stream本身不存储元素,2、Stream不会改变原有的数据源,而是产生新的数据。3、Stream的各种操作不是立即进行的,而是在调用的时候才会执行。

最后,Stream操作有以下三个步骤:

  1. 创建Stream:根据相应的数据源产生需要的Stream。
  2. 中间操作:进行各种操作,处理数据。
  3. 终止操作:执行中间操作链,产生相应的结果。

创建Stream

创建Stream有以下的几种方式:

  • 通过Collection接口新加入的方法,由列表创建一个流
  • Arrays的静态方法stream()创建一个来自数组的流
  • 通过Stream.of()方法传入一个值,创建对应的流
  • 由Stream.iterate()和Stream.generate()创建无限流

接下来就说一下这几种方法是怎么用的

Collection的新方法

Collection接口加入了以下两个新方法,用来创建不同的流

  • stream():返回一个顺序流
  • parallelStream():返回一个并行流

然后创建起来很简单:

        List<Integer> list = new ArrayList<>();
        Stream<Integer> stream1 = list.stream();
        Stream<Integer> parallelStream1 = list.parallelStream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();
        Stream<String> parallelStream2 = set.parallelStream();

Arrays.stream()

这个方法是通过数组创建对应的流

        double[] doubles = new double[21];
        DoubleStream stream1 = Arrays.stream(doubles);

        int[] ints = new int[32];
        IntStream stream2 = Arrays.stream(ints);

        String[] strings = new String[12];
        Stream<String> stream3 = Arrays.stream(strings);

其中DoubleStreamIntStreamStream的一个实现类,专门用来处理对应的数据的,另外还有一个长整型,也有对应的实现类。

Stream.of()

这个方法的参数是可变的,我们可以传入一个及以上的参数来创建流。

另外就是注意一下流的泛型,别出错了。

        int a = 23;
        double b = 23.2323;
        long c = 5465;
        String d = "ass";
        
        Stream<Object> a1 = Stream.of(a, b, c, d);

Stream.iterate()和Stream.generate()

这两个方法创建出来的都是无限流,就是如果不加以限制,这个流是无穷无尽的,会一直进行下去。

一般来说这两个方法都会对应limit()使用,不然就一直不停了。

然后接下来说这个是咋用的。

iterate()

这个方法有两个参数,第一个是流创建的基准,后面的是根据前一个值,然后生成下一个值,可以看作是递推公式。

        Stream<Integer> stream1 = Stream.iterate(33, (x) -> x + 23);

        Stream<String> stream2 = Stream.iterate("as", (x) -> x + "cc");

generate()

这个就是直接根据传入的方法创建对象,参数是一个实现Supplier接口的对象,可以使用Lambda表达式。

        Stream<Object> stream = Stream.generate(Object::new);

中间操作

中间操才是流的真正精华,这个就可以看作是一个流水线,然后对数据进行各种处理。
需要注意的是,这些中间操作并不是在原有的流基础上进行修改,而是生成新的流。

以下就是各种方法,不想看我的这个的可以直接去看API文档。

  • filter:过滤
  • limit:取前几
  • skip:跳过前几
  • distinct:去重
  • map:映射
  • flatMap:
  • sorted:排序
  • peek:

filter

这个方法就是根据传入的方法,判断这个数据是否要留下来。

其中传入的参数是一个实现了Predicate接口的对象,同样在这里可以使用Lambda表达式。

使用起来就如下:

    private User[] users = {
            new User(1, "陈二", 43, "女"),
            new User(1, "张三", 23, "男"),
            new User(1, "李四", 33, "男"),
            new User(1, "王五", 13, "女"),
            new User(1, "赵六", 51, "男")
    };

    @Test
    public void test1() {

        Stream<User> userStream = Arrays.stream(users);
        userStream.filter((u) -> u.getAge() > 20)
                .forEach( System.out :: println );

    }

这个是过滤掉年龄不足20的人。

limit

这个就是在限制原有的数据,只输出前N个,具体的多少,要看传入的参数。

另外就是在取够了数据之后会直接结束,并不会将所有的数据都进行处理。

        Stream<User> userStream = Arrays.stream(users);
        userStream.filter((u) -> u.getAge() > 20)
                .limit(2)
                .forEach( System.out :: println );

在上面的基础上,限制了只取前两名。

skip

这个和上面的数据类似,不过是跳过前n个数据,另外就是如果流中本身的数据并不够n个,返回的是空流。

用这个的时候需要注意和前面方法的位置,每次都是在新流的基础上进行处理。 例如我在limit(2)后面使用.skip(2)那输出永远都是空,原因就是在limit(2)处理之后的新流之中就只有两个元素了,再跳过两个之后就直接成空的了。其他的也类似。

这个是在过滤之后的结果中跳过前两个,然后再只输出两个。

        Stream<User> userStream = Arrays.stream(users);
        userStream.filter((u) -> u.getAge() > 20)
                .skip(2)
                .limit(2)
                .forEach( System.out :: println );

distinct

这个方法是对输入的流中的元素进行一个去重的操作,去重的依据是equals()方法,所以用这个对自定义类型去重的时候记得重写方法,不然没用,因为这样对比的是内存中的地址,不同对象的地址当然不一样。

map

这个就是做一个映射操作,原本的元素,通过输入的函数进行各种处理,最后得到结果。

其输入的是一个Function函数,同样可以使用Lambda表达式。

下面是一个小栗子,这个玩意就是将原本的User对象中的name取出来。

        Stream<User> userStream = Arrays.stream(users);
        userStream.map(User::getName)
                .forEach(System.out :: println);

flatMap

这个大致勇用法同map相同,但是如果映射函数产生了多个流,会将这些流拼接起来,然后得到一个新的流。

这是一个栗子,然后就是依次输出每个User对象的name、age、sex属性。

        Stream<User> userStream = Arrays.stream(users);
        userStream.flatMap(u -> Stream.of(u.getName(), u.getAge(), u.getSex() ))
                .forEach(System.out :: println);

sorted

没啥好说的,就是排序。

这个方法有两个重载函数,一个没有参数,一个有参数。

  • Stream sorted()
    这个虽然不需要传入参数,但是需要流中的元素都是实现了Comparable接口的类型,然后怎么排序就看是怎么实现其中的compareTo方法了。如果不实现这个接口,没有提示,在运行的时候会报错。
  • Stream sorted(Comparator<? super T> comparator)
    这个就需要传入一个自定义的比较器了,好处就是不需要实现接口。

下面是一个栗子,这个会将User对象安装年龄升序排序。

        Stream<User> userStream = Arrays.stream(users);
        userStream.sorted(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        }).forEach(System.out :: println);

peek

这个方法就是在元素输出的时候执行这个方法中的函数进行处理,注意是在每个元素输出之前才会执行这个方法。

这个栗子对每个元素会连续输出条数据。

        Stream<User> userStream = Arrays.stream(users);
        userStream.peek(System.out :: println)
                .forEach(System.out :: println);

终止操作

终止操作,就是流进行到这里就到头了,接下来就不能进行操作了。

然后大致有以下的各种方法:

  • forEach:遍历流中的数据
  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配的元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值

forEach

这个通常用来输出,但是不知是可以用来输出,传入的参数为一个实现了Consumer接口的对象。

没有栗子,上面的基本上都是用这个进行输出的。

allMatch

判断流中的元素是否全部符合条件,具体的判断条件由传入的函数判断。

这个函数的参数为实现了Predicate接口的对象,由这个对象判断是否符合条件。

这个栗子判断流中的对象,性别是否为男。

        System.out.println(userStream.allMatch((u) -> "男".equals(u.getName())));

anyMatch

同上面的相同,但是判断的是是否存在匹配的元素,也就是只要有一个就行了。

noneMatch

这个检查是否存在不匹配的元素,有一个不匹配就返回true。

findFirst

这个得到流中的第一个元素

        System.out.println(userStream.findFirst());

需要注意的是,这个方法的返回类型并不是流中原有的数据,而是一个Optional<T>类型,这个东西也是在Java8中刚出来的。

具体就对原本的数据进行了一次封装,对空指针异常的情况进行了一些处理。

findAny

得到流中的任意一个元素,大致同上。

count

获取流中有多少个元素,返回值是长整型的。

        System.out.println(userStream.count());

max

获取流中最大的元素,具体的怎么是最大的,需要根据你传入的参数进行判断。

传入的是实现了Comparator接口的对象。

        System.out.println(userStream.max(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        }));

min

获取最小的元素,用法类似上面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值