title: Java 8 Streams author: Gamehu tags:
{% asset_img computer-keyboard-34153.jpg Photo by Negative Space from Pexels %}
最近两周被平台组指名道姓拉去当了两周的苦力,写业务层代码,因为逻辑比较复杂数据输入比较多样,所以导致使用集合的概率很高,且常常伴随着过滤、排序等操作,继而用到了很多Streams提供的方法,遂做个简单记录。
Stream是Java 8中引入的新的抽象层,它提供了一些类似SQL语句的声明性方式处理数据。
流操作分为中间操作和最终操作,元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。 流管道由一个源(例如Collection,数组,生成器函数或I / O通道)组成; 随后是零个或多个中间操作,例如Stream.filter或Stream.map; 以及诸如Stream.forEach或Stream.reduce之类的终端操作。
{% asset_img java-streams.png %}
即将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道中插入节点上进行处理, 比如筛选, 排序,聚合等。
当然为什么喜欢用它还是因为Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。会让代码看起来更加简洁当然通常也会更加高效。
{% asset_img apis.png api %}
如上图api很多,其中又可以按照最开始说的分为中间操作、最终操作两类,中间(Intermediate)操作是可以零个或者多个但是最终(Terminal)操作只能有一个,能力有限我就列举一下我常用的。
可能我们需要注意的一个概念:因为一个 Stream 可以进行多次中间操作,那是不是就会对 Stream 的每个元素进行转换多次,即时间复杂度就是 N(转换次数)个 ?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。
| API | 说明 |
|---|---|
| filter | 用于按指定条件过滤元素 |
| map | map方法将每个元素映射到其相应的结果,通常用于list转换为map |
| limit | limit返回流中的前N个元素,同SQL的limit |
| sorted | 对Stream中的元素进行排序 |
| distinct | 删除重复项 |
| API | 说明 |
|---|---|
| forEach | 迭代Stream中的元素 |
| sum | 对Stream中的元素求和 |
| collect | 可以接受各种参数并将流元素累加成集合 |
| reduce | 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。其实sum等这类也可以说是reduce。 |
| max | 获取Stream中符合条件的最大值 |
| findAny | 这是一个 termimal 操作,它总是返回 Stream 的符合条件的元素,或者空。注意它的返回值类型是Optional(为了避免空指针)。 |
| anyMatch | Stream 中只要有一个元素符合传入的 条件。 |
列出几个工作中实践的例子
// 用于按指定条件过滤元素并且把符合条件的添加到指定的集合
List<CiStrategy> sorted = new ArrayList<>(cis.size());
cis.stream().filter(it -> it.getType() == StrategyType.GLOBAL).forEach(sorted::add);
// 拿输入的id到stream中比较是否存在,如果不存在则返回null
final List<String> agentEnableIds = getEnableAgenIdsByApp(query.getAppId());
String enableId = agentEnableIds.stream()
.filter(id -> agentId.equals(id))
.findAny()
.orElse(null);
//未开启xx
if (isNullOrEmpty(enableId)) {
return Collections.emptyList();
}
// 两个集合中,集合B中找到符合集合A中的数据,最终得到符合条件的元素集合
agentCmsList.stream().filter(ag -> agentEnableIds.contains(ag.getId())).collect(Collectors.toList());
// checkList的元素作为IpV4Ranges中toRange方法的参数,最终把toRange返回值转换为集合
List<String> checkList = splitter.splitToList(scopesToCheck);
List<IpRange> rangesToCheck = checkList.stream().map(IpV4Ranges::toRange).collect(toList());
// failed集合中,去重后的类型失败的有哪些
List<String> types = failed.stream().map(Quality.Metric::getType).distinct().collect(toList());
// Alarm的list集合,转换为map,key为Alarm的appId,value为Alarm
List<Alarm> dealingAlarms = dealingAlarmPage.getList();
Map<String, Alarm> dealingAlarmMap = dealingAlarms.stream().collect(Collectors.
toMap(Alarm::getAppId, Function.identity()));
// 根据状态排序,如果状态一样按照名称排序
public IBoardAppDataList sortByAppStatus(String[] statusOrder) {
return new BoardAppDataList(this.stream().sorted(new Comparator<BoardAppData>() {
private int findStatus(String[] a, BoardAppData target) {
return IntStream.range(0, a.length)
.filter(i -> String.valueOf(target.getStatus()).equals(a[i]))
.findFirst()
.orElse(-1); // return -1 if target is not found
}
@Override
public int compare(BoardAppData o1, BoardAppData o2) {
int c = findStatus(statusOrder, o1) - findStatus(statusOrder, o2);
if (c == 0) {
return o1.getAppName().compareTo(o2.getAppName());
} else {
return c;
}
}
}).collect(Collectors.toList()));
}
// 判断输入参数里是否存在任意一个满足haveXssCondition
Set<String> keys = paramsObj.keySet();
return keys.stream().anyMatch(key -> haveXssCondition(uri, paramsObj, key));
很显然我这篇仅仅是一个简单的记录文档,如果需要深入了解,还是系统的看相关的文档和源码。而且我主要用的是stream其实还有parallelStream,有兴趣的大家可以看看。
{% blockquote 陈 争云, 占 宇剑, 和 司 磊 https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html Java 8 中的 Streams API 详解 %} {% endblockquote %}