再来看看Java的新特性——Stream流

半年前开始试着使用Java的新特性,给我印象最深的就是Stream流和Optional。其中Stream提升了见解效率,让代码看起来十分清爽。java

为何要使用流?

摘要中已经说明了,为了提升开发效率。流能够帮助咱们高效操做集合,流帮助咱们经过流水线的方式对集合进行删减合并排序修改,并最终返回咱们想要的元素数据或统计数据。流水线的意思是说,一批元素不须要等待所有元素都完成某步操做,才进行下步操做,而是能够尽早进行下步操做,就好像流水线工厂同样,这为流高效运做提供了基础。流还有一个内部迭代的概念,就是把for循环显示迭代隐藏起来了,这样能够更方便的并行开发json

流的使用

准备

在使用流以前须要作些准备,首先说明两个概念:中间操做终端操做中间操做就比如生产车间的一步一步元素处理,每步处理以后,里面的元素样式或者数据结构可能发生改变了,可是它仍是流。终端操做就比如产品生产完了,要打包装箱,变成最终消费者可见的最终形态,此时,它已是产品,再也不是流了。数组

接着为了演示操做,先模拟几条数据bash

List<JSONObject> menu = new ArrayList<>();
menu.add(new JSONObject().putOpt("name","宫保鸡丁").putOpt("price","28"));
menu.add(new JSONObject().putOpt("name","鱼香肉丝").putOpt("price","30"));
menu.add(new JSONObject().putOpt("name","肉夹馍").putOpt("price","6"));
menu.add(new JSONObject().putOpt("name","煎饼").putOpt("price","6"));
复制代码

经常使用的中间操做

filter

filter应该是Stream操做里面最多见的了,过滤器顾名思义就是过滤数据用的,filter的参数能够是lambda表达式。数据结构

//好比下面这句话,就是获得全部价格小于10的食物,获得的仍是流。
//stream()方法将集合转成流
menu.stream().filter(jsonObject -> jsonObject.getInt("price")<10);
复制代码

distinct 、 limit 和 skip

distinct是去重,limit是截取前几个元素,skip是跳过前多少个元素。spa

List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.stream()
    .distinct()//通过去重,流还剩一、二、3
    .skip(1)//跳过第一个元素,流中还有二、3
    .limit(1);//截取第一个元素,流中还剩2
复制代码

map

map映射,上面的filter是将元素筛选,map则是改变元素的样式。好比,咱们想要知道全部小于10块食物的名字。code

menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price")<10)//此时仍是jsonObject
	.map(jsonObject -> jsonObject.getStr("name"));//此时变成了String
复制代码

flatMap

流的合并,能够将多个数组合并操做,这样返回元素不是流,而是具体元素自己了。orm

Stream.of(menu,foreignMenu)//此时元素是流 List<Stream>
    .flatMap(x -> x.stream())//此时元素是jsonObject List<jsonObject>
    .map(jsonObject -> jsonObject.getStr("name"))
    .distinct();
复制代码

经常使用的终端方法

allMatch、anyMatch、noneMatch、findFirst和findAny

方法 含义
allMatch 检查谓词是否匹配全部元素
anyMatch 检查谓词是否至少匹配一个元素
noneMatch 确保流中没有任何元素与给定的谓词匹配
findFirst 查找第一个符合条件的元素
findAny 将返回当前流中的任意元素
//前三个方法都是返回boolean类型
boolean allMatchBool = menu.stream()
    .allMatch(jsonObject -> jsonObject.getInt("price") < 10);
boolean noneMatchBool = menu.stream()
    .noneMatch(jsonObject -> jsonObject.getInt("price") < 10);
boolean anyMatchBool = menu.stream()
    .anyMatch(jsonObject -> jsonObject.getInt("price") < 10);
复制代码

上面个方法返回的都是boolean类型,findFirst、findAny返回的都是元素。cdn

//关于Optional,先不关心,总之是元素就对了
Optional<JSONObject> first = menu.stream().findFirst();
Optional<JSONObject> any = menu.stream().findAny();

System.out.println(first.get().toString());
System.out.println(any.get().toString());

//输出
//{"price":"28","name":"宫保鸡丁"}
//{"price":"28","name":"宫保鸡丁"}
复制代码

以上两个方法,只要找到符合条件的数据,流就提早结束了。为何都是输出第一个元素,却要实现有两个方法呢?由于并行,findAny在并行方面限制会少一些。对象

reduce

最开始的时候说了,最终的返回值能够是元素集合,也能够是统计数据(或者说概括),好比说元素求和。假设咱们须要menu中全部食品各要一份须要花多少钱。

Optional<Integer> price = menu.stream()//List<JsonObject>
    .map(jsonObject -> jsonObject.getInt("price"))//先将元素转成数字List<Integer>
    .reduce((x, y) -> x + y);
System.out.println(price.get());
复制代码

max和min

这个好理解,就是最大值和最小值嘛。效果相似于

.reduce(Integer::max)
.reduce(Integer::min)
复制代码

经常使用流汇总

其中没有展现sorted、count这个都好理解。至于collect这个后面讲,用的比较多。

流的转化

除了对象流(Stream)之外,还有一些类型流,好比说 IntStream(以 IntStream 举例,其余相似)上面求和返回的是Optional对象,那能够直接返回Integer类型吗?

//使用映射方法mapToInt()就ok了
int price = menu.stream()//Stream
    .mapToInt(jsonObject -> jsonObject.getInt("price"))//IntStream
    .sum();
//类型流转化回对象流,可使用boxed()
IntStream intStream = menu.stream()
	.mapToInt(jsonObject -> jsonObject.getInt("price"));
Stream<Integer> boxed = intStream.boxed();
//固然了IntStream中有不少int类型操做的方法,就不一一举例了,源码打开一看,见名知意
复制代码

收集器

前面讲的经常使用的中间操做,返回值都是流,还有一些中断操做,返回值都是Optional或者数值。可别忘了Stream最开始的初衷是为了解决集合操做问题。最终转化成集合使用的中断操做collect,参数是接口 Collector,里面有众多转化方法。

转换成集合

最经常使用的莫非toList() 这个方法了,将返回结果变成List。

List<JSONObject> list = menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price") < 10)
    .collect(Collectors.toList());
//固然还有toSet()等等,举一反三
复制代码

字符串拼接

比较经常使用,就是字符串连接了。使用joining()方法

String s = menu.stream()
    .filter(jsonObject -> jsonObject.getInt("price") < 10)
    .map(jsonObject -> jsonObject.getStr("name"))
    .collect(Collectors.joining(","));
复制代码

分组

根据提供的属性分组,使用 groupingBy() ,为了方便说明,给上面各类食品一个type值:

List<JSONObject> menu = new ArrayList<>();
menu.add(new JSONObject().putOpt("name","宫保鸡丁").putOpt("price","28").putOpt("type","good"));
menu.add(new JSONObject().putOpt("name","鱼香肉丝").putOpt("price","30").putOpt("type","good"));
menu.add(new JSONObject().putOpt("name","肉夹馍").putOpt("price","6").putOpt("type","normal"));
menu.add(new JSONObject().putOpt("name","煎饼").putOpt("price","6").putOpt("type","normal"));

Map<String, List<JSONObject>> type = menu.stream()
    .collect(Collectors.groupingBy(jsonObject -> jsonObject.getStr("type")));
System.out.println(type);
//输出
//{normal=[{"price":"6","name":"肉夹馍","type":"normal"}, {"price":"6","name":"煎饼","type":"normal"}], good=[{"price":"28","name":"宫保鸡丁","type":"good"}, {"price":"30","name":"鱼香肉丝","type":"good"}]}
复制代码

与分组相似的还有一个方法 partitioningBy (),分区,不过它的参数位因而boolean类型。

个人公众号

个人公众号用于博客同步

相关文章
相关标签/搜索