Java 8 Streams

最近两周被平台组指名道姓拉去当了两周的苦力,写业务层代码,因为逻辑比较复杂数据输入比较多样,所以导致使用集合的概率很高,且常常伴随着过滤、排序等操作,继而用到了很多Streams提供的方法,遂做个简单记录。

Streams

Stream是Java 8中引入的新的抽象层,它提供了一些类似SQL语句的声明性方式处理数据。

流操作分为中间操作和最终操作,元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。 流管道由一个源(例如Collection,数组,生成器函数或I / O通道)组成; 随后是零个或多个中间操作,例如Stream.filter或Stream.map; 以及诸如Stream.forEach或Stream.reduce之类的终端操作。

即将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道中插入节点上进行处理, 比如筛选, 排序,聚合等。

当然为什么喜欢用它还是因为Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。会让代码看起来更加简洁当然通常也会更加高效。

API

如上图api很多,其中又可以按照最开始说的分为中间操作、最终操作两类,中间(Intermediate)操作是可以零个或者多个但是最终(Terminal)操作只能有一个,能力有限我就列举一下我常用的。

可能我们需要注意的一个概念:因为一个 Stream 可以进行多次中间操作,那是不是就会对 Stream 的每个元素进行转换多次,即时间复杂度就是 N(转换次数)个 ?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。

中间操作(intermediate operation)

API 说明
filter 用于按指定条件过滤元素
map map方法将每个元素映射到其相应的结果,通常用于list转换为map
limit limit返回流中的前N个元素,同SQL的limit
sorted 对Stream中的元素进行排序
distinct 删除重复项

最终操作(terminal operation)

API 说明
forEach 迭代Stream中的元素
sum 对Stream中的元素求和
collect 可以接受各种参数并将流元素累加成集合
reduce 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。其实sum等这类也可以说是reduce。
max 获取Stream中符合条件的最大值
findAny 这是一个 termimal 操作,它总是返回 Stream 的符合条件的元素,或者空。注意它的返回值类型是Optional(为了避免空指针)。
anyMatch Stream 中只要有一个元素符合传入的 条件。

示例

列出几个工作中实践的例子

1
2
3
// 用于按指定条件过滤元素并且把符合条件的添加到指定的集合
List<CiStrategy> sorted = new ArrayList<>(cis.size());
cis.stream().filter(it -> it.getType() == StrategyType.GLOBAL).forEach(sorted::add);
1
2
3
4
5
6
7
8
9
10
11
// 拿输入的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();
}
1
2
// 两个集合中,集合B中找到符合集合A中的数据,最终得到符合条件的元素集合
agentCmsList.stream().filter(ag -> agentEnableIds.contains(ag.getId())).collect(Collectors.toList());
1
2
3
4
5
6
// 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());
1
2
3
4
// 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()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 根据状态排序,如果状态一样按照名称排序
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()));
}
1
2
3
// 判断输入参数里是否存在任意一个满足haveXssCondition
Set<String> keys = paramsObj.keySet();
return keys.stream().anyMatch(key -> haveXssCondition(uri, paramsObj, key));

最后

很显然我这篇仅仅是一个简单的记录文档,如果需要深入了解,还是系统的看相关的文档和源码。而且我主要用的是stream其实还有parallelStream,有兴趣的大家可以看看。

感谢: