Java8新特性之Stream

Stream简介

Stream 使用一种类似用 SQL 语句从数据库查询数据的方式来提供一种对 Java 集合的运算和表达。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

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

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。

聚合操作类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。

以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

Stream对象的获取方式

这里简单介绍常用的3种获取流对象的方式

构建一个pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
class Dog {

    private String name;
    private Integer age;
    private Integer health;

    /**
     * 下文中list数据均从此方法拿到
     * @return list集合
     */
    public static List<Dog> getDogList() {
        List<Dog> dogList = new ArrayList();
        dogList.add(new Dog("aaa", 0, 100));
        dogList.add(new Dog("bbb", 1, 80));
        dogList.add(new Dog("ccc", 2, 100));
        dogList.add(new Dog("ddd", 3, 70));
        dogList.add(new Dog("eee", 4, 100));
        dogList.add(new Dog("abb", 5, 90));
        dogList.add(new Dog("acc", 6, 100));
        dogList.add(new Dog("add", 7, 70));
        dogList.add(new Dog("aee", 8, 20));
        return dogList;

}

1.通过Collection集合来获取

stream():返回的是串行流即集合内元素顺序不变

parallelStream():返回的是并行流即集合内元素顺序随机

开发者实例

	/**
     * 通过集合获取Stream对象
     */
    @Test
    public void test01() {
        //获取List集合
        List<Dog> dogList = Dog.getDogList();
        //获取Stream(串行/并行)对象
        Stream<Dog> dogStream = dogList.stream();
        Stream<Dog> dogStream = dogList.parallelStream();
        
    }

2.通过Arrays数组工具类来获取

开发者实例

    /**
     * 通过数组工具类获取Stream对象
     */
    @Test
    public void test02() {
        //通过不同类型的数组获取Stream对象
        IntStream intStream = Arrays.stream(new int[]{1, 2, 3, 4, 5, 6, 7, 8});
        Stream<Dog> dogStream = Arrays.stream(new Dog[]{new Dog("DaHuang", 9,100), new Dog("XiaoHei", 5,100)});
    }

3.通过Stream类来获取

开发者实例

    /**
     * 通过Stream的of(T... values)获取Stream对象
     */
    @Test
    public void test03() {
        //通过不同类型的数组获取Stream对象
        Stream<Dog> dogStream = Stream.of(new Dog("DaHuang", 9,100), new Dog("XiaoHei", 5,100));
        Stream<Integer> integerStream = Stream.of(1, 2, 3);
    }

常用API

上文讲述到流的操作包含中间操作和最终操作,中间操作结束总是返还一个流对象,只有经过最终操作才可以得到最终的结果,最终操作返还的类型不一定,根据API来决定。

中间操作:filter,disinct,map,skip,limit,sorted等

最终操作:forEach,allMatch,noneMatch,anyMatch,count,max,min,reduce,collect等

迭代(forEach)

用来迭代流中的每个数据。

开发者实例

    @Test
    public void test04() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //展示流中的数据
        stream.forEach(System.out::println);
    }

过滤(filter)

用于通过设置的条件过滤出元素

开发实者例

    @Test
    public void test04() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //筛选出狗狗年龄大于5且名字中包含a的狗狗信息
        stream.filter(dog -> dog.getAge() > 5 && dog.getName().contains("a")).forEach(System.out::println);
    }

筛选(distinct)

用于去掉重复的元素。

开发者实例

    @Test
    public void test05() {
        List<Dog> dogList = Dog.getDogList();
        //添加重复元素
        dogList.add(new Dog("aee", 8, 20));
        dogList.add(new Dog("aee", 8, 20));
        Stream<Dog> stream = dogList.stream();
        //筛选,比较流中元素的equals()和hashCode()去掉重复元素
        stream.distinct().forEach(System.out::println);
    }

映射(map)

用于映射每个元素到对应的结果。

开发者实例

    @Test
    public void test05() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //只显示狗狗的姓名
        stream.map(dog -> dog.getName()).forEach(System.out::println);
    }

跳过(skip)

用于跳过流中指定的元素个数,如果流中元素不足要跳过的个数,则返回一个空流。

开发者实例

 @Test
    public void test07() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //跳过元素,跳过流中前2个元素 。
        stream.skip(2).forEach(System.out::println);
    }

截断(limit)

用于获取流中指定的元素个数。

开发者实例

    @Test
    public void test08() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //获取流中的前5个元素。
        stream.limit(5).forEach(System.out::println);
    }

排序(sorted)

sorted():自然排序,升序

sorted(Comparator<? super T>):定制排序

自然排序

sorted()

开发者实例

    @Test
    public void test09() {
        Stream<Integer> intStream = Stream.of(1, 5, 9, 7, -48, 0, 85, 3, 5);
        //自然排序并打印
        intStream.sorted().forEach(System.out::println);
    }

这里注意如果是文中Dog这种类型使用自然排序需要实现Comparable接口,重写内部排序方法的规则,否则会抛出异常。

异常案例

    @Test
    public void test10() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //自然排序Dog对象
        stream.sorted().forEach(System.out::println);
    }
	/*
	java.lang.ClassCastException: class com.wenbin.java21.Dog cannot be cast to class java.lang.Comparable...... 
 	 */

这里运行抛出异常,思考一下,如果不调用forEach()的话代码是否还可以正常运行?

答案是可以,因为文章一开始讲到,流毕竟要有终止操作才可以结束得到结果,如果只经过了中间操作,元素始终都在流中。

如果想对对象实现排序并返还那么就需要用到定制排序。

定制排序

sorted(Comparator<? super T>)

开发者实例

    @Test
    public void test11() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //定制排序Dog对象
        stream.sorted((a, b) -> {
            //按年龄排序升序
            int compare = Integer.compare(a.getAge(), b.getAge());
            //如果年龄相同则比较健康值
            if (compare == 0) {
                return Integer.compare(a.getHealth(), b.getHealth());
            }
            //返回比较结果
            return compare;
        }).forEach(System.out::println);
    }

上文中Integer类的compare(int x, int y)方法

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

匹配(allMatch/noneMatch/anyMatch)

allMatch(Predicate<? super T>):检查是否匹配所有元素

anyMatch(Predicate<? super T>):检查是否匹配其中一个元素

noneMatch(Predicate<? super T>):检查是否没有匹配的元素

开发者实例

	@Test
    public void test12() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //流中的每个元素中是否年龄都>=1
        boolean result = stream.allMatch(d -> d.getAge() >= 1 );
    }

    @Test
    public void test13() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //流中的每个元素中是否有一个年龄>=9
        boolean result = stream.anyMatch(d -> d.getAge() >= 9 );
    }

    @Test
    public void test14() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //流中的每个元素中是否年龄都没有>=9
        boolean result = stream.noneMatch(d -> d.getAge() >= 9 );
    }

查找(findFirst/findAny)

findFirst():返回第一个元素

findAny():返回任意一个元素

开发者实例

    @Test
    public void test15() {
        Stream<Dog> stream = Dog.getDogList().parallelStream();
        //返回第一个元素 与串行流无关 返回的是dogList中的的第一个
        Optional<Dog> first = stream.findFirst();
    }

    @Test
    public void test16() {
        Stream<Dog> stream = Dog.getDogList().parallelStream();
        //返回流中的任意元素 但是经过多次测试 返回的总是同一个元素
        Optional<Dog> any = stream.findAny();
    }

函数(count/max/min)

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

max(Comparator<? super T>):返回流中元素的最大值

min(Comparator<? super T>):返回流中元素的最小值

开发者实例

    @Test
    public void test17() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //返回流中元素的个数 空流则返回0
        long count = stream.count();
    }

    @Test
    public void test18() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //返回流中年龄最大的对象
        Optional<Dog> max = stream.max((a, b) -> {
            return Integer.compare(a.getAge(), b.getAge());
        });
    }

    @Test
    public void test19() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //返回流中最小的年龄
        Optional<Integer> min = 
            stream.map(d -> d.getAge()).
                min((a, b) -> Integer.compare(a, b));
    }

归约(reduce)

reduce(BinaryOperator<T>):将流中的元素结合起来得到一个值

reduce(T,BinaryOperator<T>):将流中的元素结合起来得到一个值,每次结合时都会额外带上第一个参数T的值

开发者实例

    @Test
    public void test20() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //返回所有狗狗的健康度总和
        Optional<Integer> reduce = stream.map(Dog::getHealth)
                .reduce((a, b) -> a + b);
        //返回所有狗狗的名字总和
        //Optional<String> reduce = stream.map(Dog::getName)
        //.reduce((a, b) -> a + b);
    }

    @Test
    public void test21() {
        Stream<Dog> stream = Dog.getDogList().stream();
        // //返回所有狗狗的健康度总和 参数中的10就是每次操作的初始值 共9次故结果是健康度总和+90
        Integer reduce = stream.map(Dog::getHealth).reduce(10, (a, b) -> a + b);
    }

收集(collect)

将流中的所有元素转化成其他形式

开发者实例

    @Test
    public void test22() {
        Stream<Dog> stream = Dog.getDogList().stream();
        //将流中的元素转化成list(元素有序)和set(元素无序)
        //List<Dog> collect = stream.collect(Collectors.toList());
        //Set<Dog> collect1 = stream.collect(Collectors.toSet());
        //将流中的元素转化成map,键为狗狗的名字,值为对应的健康值
        Map<String, Integer> collect = stream.collect(Collectors.toMap(d -> d.getName(), d -> d.getHealth()));
    }

综合实例

获得一个List储存狗狗,List里面存储所有狗狗年龄和健康值符合(0-4岁健康值在100,其他岁数健康值在100-50以内的,每个年龄只储存一次,相同年龄则存储健康值最大的,健康值也相同以先储存的为准),List里按年龄从大到小储存。

要求所有操作都以流的API为来完成

这里为了效果明显,用到新的元素集合

@Data
@AllArgsConstructor
@NoArgsConstructor
class Dog {

    private String name;
    private Integer age;
    private Integer health;

    public static List<Dog> getDogList1() {
        List<Dog> dogList = new ArrayList();
        dogList.add(new Dog("0岁1号", 0, 100));
        dogList.add(new Dog("0岁2号", 0, 20));
        dogList.add(new Dog("1岁1号", 1, 80));
        dogList.add(new Dog("1岁2号", 1, 50));
        dogList.add(new Dog("2岁1号", 2, 100));
        dogList.add(new Dog("2岁2号", 2, 90));
        dogList.add(new Dog("3岁1号", 3, 70));
        dogList.add(new Dog("3岁2号", 3, 100));
        dogList.add(new Dog("4岁1号", 4, 100));
        dogList.add(new Dog("5岁1号", 5, 90));
        dogList.add(new Dog("5岁2号", 5, 40));
        dogList.add(new Dog("5岁3号", 5, 100));
        dogList.add(new Dog("6岁1号", 6, 50));
        dogList.add(new Dog("6岁2号", 6, 100));
        dogList.add(new Dog("7岁1号", 7, 70));
        dogList.add(new Dog("7岁2号", 7, 30));
        dogList.add(new Dog("8岁1号", 8, 20));
        return dogList;
    }

}
	@Test
    public void test23() {
        //定义一个map用来存储元素的年龄和健康值
        Map<Integer, Integer> map = new HashMap();
        Dog.getDogList1()
                //拿到所有元素流对象
                .stream()
                //先把所有的元素按年龄升序,年龄相同健康值升序排序 用于后面过滤来确定如果map中有该元素那么map里的健康值一定是最大的
                .sorted((d1, d2) -> {
                    int compare = Integer.compare(d1.getAge(), d2.getAge());
                    if (compare == 0) {
                        return -Integer.compare(d1.getHealth(), d2.getHealth());
                    }
                    return compare;
                })
                //开始过滤 每个年龄只留下健康值最大的元素
                .filter(d -> {
                    //判断当前元素是否在map中 如果在返回对应的健康值
                    Integer health = map.get(d.getAge());
                    //开始判断
                    if ((d.getAge() <= 4 && d.getAge() >= 0 && d.getHealth() == 100)||(d.getAge() > 4 && d.getHealth() >= 50 && d.getHealth() <= 100)) {
                        //判断当前元素的年龄是否在map里,如果不在map里面则 添加到里面
                        if (health == null) {
                            map.put(d.getAge(), d.getHealth());
                            return true;
                        }
                        //在里面直接过滤掉当前元素 因为相同年龄元素,是按健康值降序排序
                        return false;
                    }
                    return false;
                })
                //此时流中数据是按年龄逆序的 需要再次排序改为正序
                .sorted((d1, d2) -> -Integer.compare(d1.getAge(), d2.getAge()))
                //流的最终操作,将流对象转化为List集合
                .collect(Collectors.toList())
                //遍历该集合
                .forEach(System.out::println);

    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值