美工素材网站,建站模板 discuz,网站旁边的小图标怎么做的,重庆 网站设计外包公司从这一篇文章开始#xff0c;我们会由浅入深#xff0c;全面的学习stream API的最佳实践#xff08;结合我的使用经验#xff09;#xff0c;本想一篇写完#xff0c;但写着写着发现需要写的内容太多了#xff0c;所以分成一个系列慢慢来说。给大家分享我的经验的同时我们会由浅入深全面的学习stream API的最佳实践结合我的使用经验本想一篇写完但写着写着发现需要写的内容太多了所以分成一个系列慢慢来说。给大家分享我的经验的同时也促使我复习每一个细节大家共同进步。
Stream是什么
Java 8新增了一个API叫做Stream Stream的英文可以理解为流动的液体可能很多人一听脑子里的第一印象就是流式计算不自觉地就心生畏惧感觉非常的高深莫测。其实这就是一个辅助处理集合数据的工具类工具的更新必然带来的是生产力的提升这里的生产力代表的就是整洁优雅的代码更高的灵活度更好的性能。相信各类的技术文章包括博客和书籍已经写过无数遍了。比如下面摘录《Java 8实战》关于流的描述 流是Java API的新成员它允许你以声明性方式处理数据集合通过查询语句来表达而不是临时编写一个实现。就现在来说你可以把它们看成遍历数据集的高级迭代器。此外流还可以透明地并行处理你无需写任何多线程代码了 这段话的表述个人感觉类似于抓手、赋能、心智之类的PPT黑话看着挺高级的也能懂一些但也不是很懂反正如果对于不知道Stream的人并不能建立直接的理解。
所以流到底是什么呢是一个接口。让我们看看它的声明
public interface StreamT extends BaseStreamT, StreamT {StreamT filter(Predicate? super T predicate);R StreamR map(Function? super T, ? extends R mapper);void forEach(Consumer? super T action);...
}就是个接口然后这个借口有一些抽象方法filtermapforEach等等。我们可以看到有些方法返回了新的Stream有些直接是void。这个接口用来干什么用呢处理集合数据。为什么这么说看下面一个Collection接口的方法
public interface CollectionE extends IterableE {...default StreamE stream() {return StreamSupport.stream(spliterator(), false);}
}那么所有继承了Collection的接口都可以直接创建Stream然后再执行Stream里面的操作。所以这么看下来首先得承认书中的表述是高度抽象且精炼的这是书籍该做的事情。但从易于理解的角度我觉得可以说是简洁高效安全的处理集合数据的工具类。如下图所示Stream是一个中间过程。
需要注意的点
首先Stream不是一个数据结构它不存储任何数据它是一种数据处理工具代表了一种能力。Stream不会对处理的数据本身做任何修改永远都是返回新的Stream或者最终的处理结果。Stream可以有多个中间操作但只能有一个终端操作因为终端操作就求值了。一个Stream只能用一次不能多次复用。因为它不存储数据只是一个转换能力。
能力范围
Stream随着Java 8的发布已经8年多了在我有限的职业生涯里碰到的一些职场新人依然有些人觉得使用for或者iterator来遍历集合更易读易懂。但如果他真正了解Stream所蕴含的能力后应该会转变想法。下面简单介绍一下Stream都提供了什么样的能力。
1. 生成流
java.util.stream.Stream#of(T… values) 。首先stream接口本身提供了一个静态默认方法可以直接创建这里的可变参数会被解析成一个数组。java.util.Collection#stream()java.util.Arrays#stream(T[] array)java.nio.file.Files#list(Path dir)java.nio.file.Files#lines(Path path)
可以看到可以操作stream的对象基本为List或者Array.
2. 筛选和切片
这可能是用的最多的功能。对应的方法为
filter接受一个Predicate断言函数用来遍历元素是否符合断言条件。可以简单的理解为一个过滤器。distinct无参数将所有元素去重和数据库的distinct关键词能力一样。limit接受一个int型长度字段表示要保留多少个元素需要注意的时候limit并不排序。skip和limit相对应接受一个int型长度字段表示跳过多少个元素也不排序。 下面举个例子
Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).filter(x - x.startsWith(a)).distinct().skip(1).limit(3).forEach(System.out::println);}
// output:
a3
a4
a13. 映射/转换
这里主要是mapmap代表了一种对应关系即地图坐标与实际地点的对应关系我们有了经纬度就可以准确的找到地址这个例子可以很形象的解释map命名的由来和功能。
map接受一个Function作为参数即输入一个值返回另一个值满足转换的语义。flatmap同样接受一个Function作为参数不同的是这个Function中有一个参数是一个stream返回的也是一个stream意为将多个stream连成一个stream。
同样举个简单的例子
Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).filter(x - x.startsWith(a)).map(String::toUpperCase).forEach(System.out::println);
//output
A2
A3
A4
A2
A1ListString list Stream.of(Hello, world!).map(s - s.split()).flatMap(Arrays::stream).collect(Collectors.toList());
System.out.println(list);
//output
[H, e, l, l, o, w, o, r, l, d, !]4. 查找和匹配
这里的能力可以认为是一个加强版的contains方法具备多种查找匹配能力。
allMatch返回boolean接受一个Predicate断言确认全部元素均满足这个条件则返回true否则返回falseanyMatch与allMatch类似但从语义上可以区分只要任意元素满足条件即可noneMatch同样要求没有任何元素满足条件findFirst返回一个Optional里面是满足条件的第一个元素findAny返回Optional里面是满足条件的任一元素
这里需要解惑的是findAny与findFirst的区别因为这两个都是找到满足条件的元素就返回但findFirst会在限制并行流的计算会严格按照集合中元素的顺序来依次查找。findAny就不会有这个限制。如果非并行计算场景这二者并无区别。
下面依旧举简单的例子说明
boolean b1 Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).anyMatch(x - x.startsWith(a));System.out.println(b1);
//output: trueString s2 Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).filter(x - x.startsWith(a)).findFirst().get();System.out.println(s2);
//output: a2String s3 Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).filter(x - x.startsWith(a)).findAny().get();System.out.println(s3);
//output: a2//换成并行流
String s4 Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).parallel().filter(x - x.startsWith(a)).findFirst().get();System.out.println(s4);
//output: a2String s5 Stream.of(d2, a2, b1, a3, c1, a4, a2, a1).parallel().filter(x - x.startsWith(a)).findAny().get();System.out.println(s5);
//output: a45. 归约
归约是一个比较复杂的数学理论通常是用于将一个未知的问题转换成另一些已知问题同时这些已知的问题和未知的问题存在某种关联。这里不做详细探讨。在Stream API有一些方法就是用的类似的归约的思想将大的集合计算分解成小的函数计算并最终合成结果。
reducecollect 这两个方法都很重要且都是终端操作执行完即返回流的计算结果。我们逐个来说先看reduce。reduce的英文含义为减少、归纳在stream接口中的定义如下
T reduce(T identity, BinaryOperatorT accumulator);OptionalT reduce(BinaryOperatorT accumulator);U U reduce(U identity, BiFunctionU, ? super T, U accumulator, BinaryOperatorU combiner);这样的方法签名如同天书先看一个简单的例子
Integer i Stream.of(1, 4, 6, 7, 9).reduce(1, (sum, i) - sum i);
System.out.println(i);其中reduce我传了2个参数
1表示初始值可以不给不给的话默认从流的第一个元素开始计算但返回是就是Optional(sum, i) - sum i表示计算函数每次计算的结果都会暂存在sum中i则是下一个元素 所以总的来说这是一个迭代归纳的过程将多个元素的流按照自己制定的计算规则变成一个元素。不仅仅可以做述职运算也可以实现复杂对象的转换先看例子此例来源于与廖雪峰老师的网站并稍做修改具体链接https://www.liaoxuefeng.com/wiki/1252599548343744/1322402971648033
ListString props Lists.newArrayList(profilenative, debugtrue, loggingwarn, interval500);MapString, String map props.stream().map(kv - {String[] ss kv.split(, 2);MapString, String m Maps.newHashMap();m.put(ss[0], ss[1]);return m;}).reduce(new HashMap(), (m, kv) - {m.putAll(kv);return m;});map.forEach((k, v) - System.out.println(k v));//output:
logging warn
interval 500
debug true
profile native第一个map执行完之后返回了多个小map这里使用reduce进行一个map的累加
new HashMap()是初始值一个空map(m, kv) -中m是暂存累加结果kv表示下一个元素map 以上看来reduce的使用场景应该会很广泛尤其是多个集合合成一个大集合的场景。
小结
本文介绍了stream是什么、创建stream的方法、stream的一些基本API的能力和reduce方法的使用。作为stream最佳实践的开篇先从stream的基础开始写后续会逐步深入并总结我个人使用下来的最佳实践希望大家持续关注共同学习。