文章目录
Stream是Java8中处理集合的抽象概念,可以执行复杂的查找、过滤和映射等操作。在
操作API
Stream API 提供了一种高效且易于使用的处理数据的方式:
- 不是数据结构,不会保存数据;
- 不修改数据源,操作结果会保存到结果对象中;
- 惰性求值:中间处理过程只是操作记录,不会立即执行;而是等到终止操作时才会进行实际计算;
接口中函数参数可以传递Lambd表达式或者签名相同的类函数(此时需要ClsName::funName
样式),以groupingBy为例:
// 在流元素是字符串时,以下两者是等价的
groupingBy(String::length);
groupingBy(z->{return z.length;});
分类
操作接口可分为:
- 无状态类:处理操作不受之前元素影响;
- 有状态类:操作需拿到所有元素后才会继续下一步处理;
- 非短路操作:必须处理所有元素才会得到最终结果;
- 短路操作:找到符合条件的元素即得出最终结果;
Stream API | ||
中间操作 | 无状态 |
|
有状态 |
| |
结束操作 | 非短路操作 |
|
短路操作 |
|
接口说明
映射
-
map:接收源中的元素,返回做为结果集中的元素;
-
flatMap:与map类似,但当元素为stream时,把多个stream合成一个做为结果返回。
List<String> lstAll = Arrays.asList("hello", "world");
// 输出两个数字:[h, e, l, l, o]与[w, o, r, l, d]
lstAll.stream()
.map(s->{return s.split("");})
.forEach(s->{
System.out.println(Arrays.toString(s));
});
// 输出每个字符:h\n e\n。。。
lstAll.stream()
.flatMap(s->{return Stream.of(s.split(""));})
.forEach(System.out::println);
创建stream
Java8中的大部分集合都有stream()接口,可直接获取stream。
Collection.stream
Java中集合都继承或实现了Collection.stream()接口:
Arrays.asList(3, 5, 7, 16, 18)
.stream()
.filter(z->{return z%2==0;})
.forEach(System.out::println); // 输出偶数
Arrays.stream
通过Arrays.stream()可生成数组的流:
Integer[] array = new Integer[]{3, 4, 8, 16, 19};
long count = Arrays.stream(array)
.filter(i -> i > 10)
.count();
Stream.of
通过Stream.of()可把指定的元素(单个、或多个)变成流:
List<Integer> lstAll = Stream.of(1, 3, 5, 7)
.map(z -> {
return z * 2;
})
.collect(Collectors.toList());
无限流
对于无限流,需要通过limit来限制最终流的长度。
Stream.generate
通过Stream.generate()可生成无限长度的流(根据指定的规则):
Stream.generate(new Random()::nextInt)
.limit(5).forEach(System.out::println);
// 输出5个随机数
Stream.iterate
通过Stream.iterate()也可生成无限长度的流,其接受一个参数与一个生成函数:
Set<Integer> setAll = Stream.iterate(1, n->{return n*2;})
.limit(5)
.collect(Collectors.toSet());
System.out.println(setAll);
Collectors
根据不同的策略将元素收集归纳起来,比如最简单常用的是将元素装入Map
、Set
、List
等可变容器中。
归并到数组
List可通过toArray直接转,stream中也有toArray。
List<Integer> lstAll = Lists.newArrayList(1,2,3,4,5,6);
Integer[] aryInt = lstAll.toArray(new Integer[0]);
String[] aryStr = lstAll.stream()
.map(Objects::toString)
.toArray(String[]::new);
归并到集合
toXXX
- toCollection:将流中的元素全部放置到一个集合(具体集合类型由参数决定,如
Collectors.toCollection(LinkedList::new)
); - toList:转换为List返回;
- toSet:转换为Set返回;
- toMap:转换为Map返回;
List转Map
Collectors.toMap可方便地把其他集合转为Map;
List<String> lstTest = Arrays.asList("a123", "a456", "b789", "b890");
lstTest.stream()
.collect(Collectors.toMap(
e -> e.substring(0, 1), // Key
e -> e, // value
(oldV, curV) -> curV, // key重复时处理方式,替换掉
HashMap::new) // 可选项,使用map类型(如ConcurrentHashMap::new)
)
.forEach((k, v) -> System.out.println(k + "=" + v));
Map转List
Map中keySet、values、entrySet,分别是键、值、键值对的集合。
Map<Integer, CountData> mapData = new ConcurrentHashMap<>();
// ...
List<CountData> lstValue = mapData.values().stream()
.collect(Collectors.toList());
mapping
相当于先map再转换为集合:map(XX).Colcollect(Collectors.toXX())
lstTest.stream().collect(
Collectors.mapping(
Integer::valueOf, // map方式
Collectors.toList()));
分组
groupingBy
生成一个拥有分组功能的Collector:默认是生成HashMap,且同一组中元素为ArrayList。
List<String> lstTest = Arrays.asList("a123", "a4561", "b789", "b8901");
lstTest.stream().collect(
Collectors.groupingBy(
String::length, // 分组方式
HashMap::new, // 结果集Map类型
Collectors.toSet())) // Map中Value(同一组中元素)集合类型
.forEach((k, v) -> System.out.println(k + "=" + v));
partitioningBy
将流中的元素按照给定的校验规则的结果分为两个部分:满足条件与不满足条件的;Map的键值为true与false。
List<String> lstTest = Arrays.asList("a123", "a4561", "b789", "b8901");
lstTest.stream().collect(
Collectors.partitioningBy(
e -> e.length() > 4, // 分类方式
Collectors.toSet())) // Map中Value(同一组中元素)集合类型,默认List
.forEach((k, v) -> System.out.println(k + "=" + v));
计算与统计
- counting:计数
- minBy/MaxBy:获取最小/最大值的Optional结果;
- summingInt/summingLong/summingDouble:求和;
- averagingInt/averagingLong/averagingDouble:求平均值;
默认情况下,Stream中的Lambda表达式不允许修改外部的变量;为此可通过CAS变量做计数,或者通过数组变量方式处理:
List<Integer> lstAll = Lists.newArrayList(1,2,3,4,5,6);
AtomicInteger count = new AtomicInteger(0);
Boolean[] hasLarge = new Boolean[1]; // 也可以使他AtomicBoolean
hasLarge[0] = false;
lstAll.forEach(z->{
if(z>3){
hasLarge[0] = true;
count.incrementAndGet(); // 普通Integer是无法修改的
}
});
reduce
reducing是一个收集器(操作),可以用在多层流、下游数据分组或分区等场合。count(),max(),min(),sum()等是预定义好的收集器。
Comparator<Person> funHeight = Comparator.comparingInt(Person::getHeight);
Map<City, Optional<Person>> tallestByCity = personList.stream().
collect(groupingBy(
Person::getCity,
reducing(BinaryOperator.maxBy(funHeight))));
获取每个城市中最高者
collectingAndThen
在归并结果后,再对结果做进一步处理。
lstTest.stream().collect(
Collectors.collectingAndThen(
Collectors.groupingBy(String::length),
z -> z.size()));
分组后,再获取分组数量(总共有几个分组)。