浅谈 java8 Stream

java8 出来了很久了。新特性 Lambda 表达式Stream 在编码时,写起来“神采飞扬”。一句真香万岁。当然会用当然是前提,我们不能仅仅只是会用而已。用的更“骚气”才是重点。

首先,流是什么?流可以理解为集合类的迭代器,当然不是很明确。但可以这么理解,当然他们还有很多不同,可以参考官网文档。提供的API可以声明式的处理数据集合,组合Lambda表达式简洁的处理数据,类似于排序不需要临时写实现。


	 @Data
	 @Builder
    public static class Person{
        /***
         * 名称
         */
        private String  name;
        /**
         * 年龄
         */
        private int age;

        /**
         * 爱好
         */
        private List<Hobby> hobbies = Lists.newArrayList();
        /**
         * 爱好
         */
        enum Hobby{
            /**
             * 打球
             */
            BALL ,
            /**
             *  玩游戏
             */
            GAME,
            /**
             * 跑步
             */
            RUN,
            /**
             * 唱歌
             */
            SINGING,;    
        }
    }

        List<Person> personList =  Lists.newArrayList(
                Person.builder().age(10).name("张三").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.RUN)).build(),
                Person.builder().age(11).name("李四").hobbies(Lists.newArrayList(Person.Hobby.GAME, Person.Hobby.RUN)).build(),
                Person.builder().age(11).name("王五").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.SINGING, Person.Hobby.GAME)).build(),
                Person.builder().age(19).name("小明").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.GAME, Person.Hobby.SINGING)).build(),
                Person.builder().age(20).name("小鹏").hobbies(Lists.newArrayList(Person.Hobby.SINGING, Person.Hobby.GAME)).build());

比如我们需要将Person集合按照年龄排序。临时实现

		 // 临时实现
        personList.sort(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge().compareTo(o2.getAge());
            }
        });
        // java8 steam
        personList = personList.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
        

对比一下,写法上有差别,但是比如再根据爱好过滤数据。在原来的方式里我们需要写个for循环过滤数据,而且还需要定义临时对象存储过滤后的对象。利用stream


        personList = personList.stream()
                .filter(person -> person.getHobbies().contains(Person.Hobby.GAME))
                .sorted(Comparator.comparing(Person::getAge))
                .collect(Collectors.toList());

再比如说,我们需要取前两个人的数据。原来的方式需要subList()。而steam 仅需要加一个limit即可,默默的说一句真香。

        personList = personList.stream()
                .filter(person -> person.getHobbies().contains(Person.Hobby.GAME))
                .sorted(Comparator.comparing(Person::getAge))
                .limit(2)
                .collect(Collectors.toList());

如果有需求可以写一火车,换成原来的写法,可能就是一个灾难(当然不可能有这样的需求)。

通过这个例子我们可以理解操作流
  • stream是一个流水线,可以组合我们的条件;(灵活)
  • 省掉了我们很多实现,stream 利用了内部迭代,代替我们实现了很多工作; (简洁、易读)
  • 我们把stream 替换成parallelStream Returns a possibly parallel {@code Stream} with this collection as its source. It is allowable for this method to return a sequential stream. 还可以并行操作;(性能好)
总结&升华

我们可以把以上的集合操作理解为三部分。第一个就是转换成Stream。第二个就是中间操作,可以自由组合(当然不是随意组合。比如在过滤完数据,下一次操作要求过滤前的数据那是不可能的,再一个我们把集合类分组转换成map。下次操作必须要根据这个map的数据结构进行组合计算)。第三个就是collect 按照官方的解释是terminal operation

当一个terminal operation操作结束后,我们可以说这个流已经被消费了。我们可以写代码测试一下。

       Stream<Person> personStream = personList.parallelStream();
        personStream.forEach(person -> {
            System.out.println(JSON.toJSON(person));
        });
        personStream.forEach(person -> {
            System.out.println(JSON.toJSON(person));
        });

返回:

{"hobbies":["BALL","SINGING","GAME"],"name":"王五","age":11}
{"hobbies":["SINGING","GAME"],"name":"小鹏","age":20}
{"hobbies":["GAME","RUN"],"name":"李四","age":11}
{"hobbies":["BALL","RUN"],"name":"张三","age":10}
{"hobbies":["BALL","GAME","SINGING"],"name":"小明","age":19}
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:583)
	at com.navinfo.platform.common.StreamTest.main(StreamTest.java:100)
	

stream has already been operated upon or closed 流已经被消费了或者被关闭了,forEach也是一个terminal operation。 当我们多次执行的时候你可以看出。为什么异常可能在前面,我们的输出在后面。因为我们声明的是parallelStream并行流。如果可以详细看到并行流的效果时 我们可以personList.parallelStream().forEach()进行数据输出测试。从有序集合生成流时会保留原有的顺序,利用personList.stream().forEach() 进行数据输出测试。

总结
  • 流支持一系列操作进行组合元素,类似于流水线;
  • 流分为中间操作和terminal operation 当有类型有forEach、count 、collect等terminal operation 可以理解为流已经被消费完了。流只能进行一次terminal operation操作;
  • 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象了(参考其他文章总结);
  • 流中的元素是按需计算的(参考其他文章总结);
扩展

如果有些数据在内存分页,当然并非特殊需求也不会在内存中分页,我们可以利用stream提供的API进行分页操作。转换为Collcetion.stream() 进行一系列filter sorted 等等中间操作,然后对“结果流”进行分页操作,结合skip 、limit 中间操作实现内存分页操作。很简单脱离list.subList操作远离数组越界错误(IndexOutOfBoundsException

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值