java8新特性之stream

stream是目前大部分公司在项目中使用的java8新特性的主要形式。小隐的情况也是这样。但是当你使用stream时间越长时,越发的感觉自己对stream了解的越少。所以这里仅整理个人拙见和经验。部分内容参考了《java8实战》。
《java8实战》一书对于java8新特性的讲解甚是详细,完全可以作为你学习java8的指导书籍,希望大家有时间可以完整的去学习一下这本书。

什么是流

我们都知道集合是java是用最多的API,你可以想象,你的java项目离开集合之后还能干什么。
但是java的集合操作却并不是那么美丽。当你想要对集合的数据进行处理的时候绝大多数情况下你不得不遍历整个集合,即使这个遍历的写作形式你已经习惯,包括1.5引入增强for循环,我们仍然不得不承认这种方式很别扭。我们应该知道相较与数据库而言这种差距就非常明显了。主流数据库中直接使用声明方式就能拿到你想要的数据,类似 select id from test_one; 不过这也不能怪集合,因为java的集合是数据结构,主要目的是是以特定的时间/空间复杂度存储和访问元素。而流的目的在于表达计算。
那你有没有期望过java也能直接通过显示声明非方式去获取这些数据?你可能没期望过。。。但是,如果能这样,你不相信你会上瘾么?我相信你会。
java8新引入的流就允许你以声明式的处理集合中的元素:直接说你想要干什么就行。
流的简短定义就是“从支持数据处理操作的源生成的元素序列”;

流的应用

实现了Collection接口的集合(这里这样说是因为依旧有很多人争论Map是否是集合,追根溯源Map和Collection其实都是集合),直接使用default方法.stream()或.parallelStream()方法就能获得一个Stream流对象。其中.parallelStream()获得的流会利用分支/合并框架进行并发操作,他会根据你的机器内核数自动并发处理,当然你可以通过系统属性java.util.concurrent.ForkJoinPool.common. parallelism来改变线程池大小,如下所示:
System.setProperty(“java.util.concurrent.ForkJoinPool.common.parallelism”,“12”);
流操作包括中间操作和终端操作。
返回另一个流的操作我们称为中间操作。
产生流结果的操作我们称为终端操作。
一个流操作一般包含(中间操作不是必须包含):
一个数据源(如集合)来执行一个查询;
一个中间操作链,形成一条流的流水线;
一个终端操作,执行流水线,并能生成结果。

部分常用的中间操作和终端操作
在这里插入图片描述

我使用流的时候大多是对流数据进行过滤筛选截取和映射还有最长用的遍历
下面以一个例子为示 (下列代码放在一个类中)

 /**
    * 定义要使用的类,这里为了方便使用嵌套类
    */
   private static class Phone {
       private String brand;
       private Integer price;
       private Boolean canUse;
       public String getBrand() { return brand; }
       public void setBrand(String brand) { this.brand = brand; }
       public Integer getPrice() { return price; }
       public void setPrice(Integer price) { this.price = price; }
       public Boolean getCanUse() { return canUse;}
       public void setCanUse(Boolean canUse) { this.canUse = canUse; }
   }
   @org.junit.Test
   public void test29() throws Exception {
       List<Phone> list = new ArrayList<>();
       //如有需要自己填充数据

//-------------这些中间操作返回结构都是另一个流对象,这里为了距离方便进行了统一终端操作
       //过滤出华为手机
       List<Phone> list2 = list.stream().filter(phone -> "HUAWEI".equals(phone.getBrand())).collect(Collectors.toList());
       //进行去重
       List<Phone> list3 = list2.stream().distinct().collect(Collectors.toList());
       //跳过前10部手机
       List<Phone> list4 = list3.stream().skip(10L).collect(Collectors.toList());
       //再截取跳过之后的前十部手机
       List<Phone> list5 = list4.stream().limit(10).collect(Collectors.toList());

//---------------第一种处理方式
       //取到这些手机的价格(上一篇中已经提到过当lambda只是调用了一个已经声明过的方法,为了增加可读性和进一步简化,可以使用方法引用)
       List<Integer> list6 = list5.stream().map(phone -> phone.getPrice()).collect(Collectors.toList());
       //List<Integer> list6 = list5.stream().map(Phone::getPrice).collect(Collectors.toList());
       //统计这几部手机的价格总和
       Integer sum = list6.stream().reduce(0, (a, b) -> a + b);
       //Integer sum = list6.stream().reduce(Integer::sum);
       //终端操作拼接
       //Integer sum = list.stream().filter(phone -> "HUAWEI".equals(phone.getBrand())).distinct().skip(10L).limit(10).map(phone -> phone.getPrice()).reduce(0, (a, b) -> a + b);
//--------------第二种处理方式
       //把这些手机根据是否可用分组
       Map<Boolean, List<Phone>> booleanAndPhoneList = list5.stream().collect(Collectors.groupingBy(phone -> phone.getCanUse()));
       //Map<Boolean, List<Phone>> booleanAndPhoneList = list5.stream().collect(Collectors.groupingBy(Phone::getCanUse));
       //终端操作拼接
//        Map<Boolean, List<Phone>> booleanAndPhoneList = list.stream().filter(phone -> "HUAWEI".equals(phone.getBrand())).distinct().skip(10L).limit(10).collect(Collectors.groupingBy(phone -> phone.getCanUse()));
//-------------根据价格排序并返回顺序结合
       List<Phone> list7 = list5.stream().sorted(Comparator.comparing(Phone::getPrice)).collect(Collectors.toList());
//        List<Phone> list7 = list.stream().filter(phone -> "HUAWEI".equals(phone.getBrand())).distinct().skip(10L).limit(10).sorted(Comparator.comparing(Phone::getPrice)).collect(Collectors.toList());
   }

其实这些方法会在我们慢慢的积累中变成我们的肌肉记忆,我们也会慢慢的用的越来越理所应当。
这其中的一些更细致的内容也会慢慢的掌握起来,包括流的扁平化、多级分组并行流等。
不过这其中的坎坷之路或多时候还是要自己趟一遍才能记得更深刻吧。我也开了一篇博客记录我再使用java8新特性中遇到的一些小坎坷,当你遇到问题了也可以去哪里找找有没有解决办法。

小隐小故事——玩偶和真人的区别

作为一个程序员其实我们大多数站的角度都是教官角度,而软件不外乎是对数据的操作。
当我们只有集合提供的方法时,我们的数据就像是我们的玩偶,我们对他们发号施令,但是具体操作还要我们一个个去进行。
在这里插入图片描述
我们拿到一个小兵,发现是个步兵,我们放到马路左边,我们拿到下一个小兵,发现是一个残兵,我们不忍心,就把他又放了回去,我们又去拿下一个。。。。直到我们从头拿到尾把所有的检查一个遍之后,我们知道我们已经找到了所有的身体健全的步兵。

当我们有了Stream API 我们成为了真正的指挥官。
在这里插入图片描述

我们只需下发命令,“所有的身体健全的步兵站到马路左边!”那些身体健全的步兵数据就会全部到左边。
所以你觉得我们是应该玩那些玩偶还是指挥这些真正的战士?!
拥抱Stream吧,可能你玩了很久玩偶了,而且你给这些战士下发命令的时候他们可能还有些问题,但你应该明白这是你命令的问题,因为这些战士已经被无数个优秀的指挥官打磨了万遍。年轻的指挥官,好好学习吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值