Skip to content

Latest commit

 

History

History
241 lines (165 loc) · 11.6 KB

Stream.md

File metadata and controls

241 lines (165 loc) · 11.6 KB

Stream 流

Stream是 JDK8 新增的接口,不像集合Collection用于有效管理和访问元素,流用于对结合进行管道式处理和集合运算,有利于简化代码和提高计算效率。

链式操作

distinct:不同的元素流

filter:符合断言Predicate的元素流

flatMap:展平

flatMapToDouble:展平为 double 流

flatMapToInt:展平为 int 流

flatMapToLong:展平为 long 流

limit:截断流

map:给定Function返回的元素的流

mapToDouble:给定Function返回的 double 流

mapToInt:给定Function返回的 int 流

mapToLong:给定Function返回的 long 流

peek:消费者Customer对每个元素

skip:丢弃前n个元素

sorted:根据比较器Comparator排序

动态规约结果

collect:动态规约操作

元素结果(Optional

findAny:任意一个

findFirst:第一个

max:根据比较器Comparator的最大元素

min:根据比较器Comparator的最小元素

reduce:根据累加器BinaryOperator聚合

boolean 结果

allMatch:所有元素满足断言Predicate

anyMatch:任意元素满足断言Predicate

noneMatch:没有一个元素断言Predicate

数值结果

count:元素数量

reduce:根据初始值和累加器BinaryOperator聚合

基本数据类型流特有的操作

Arrays.stream(new Int[])可以创建一个IntStream

mapToObj:返回一个对象类型的流

其他实例方法

forEach:循环(不保证顺序)

forEachOrdered:循环(按照顺序)

toArray:返回Object或指定类型的数组

静态方法

concat:连接两个流

empty:返回一个空的流

generate:由一个生产者Supplier生成无限无序的流,一般用于生成随机流

iterate:使用初始种子生成无限有序的流

of:使用一个或多个元素构造流

Stream (Java Platform SE 8 ) (oracle.com)

Tip

默认的流都是并行流,即元素会被多线程处理,串行流:

list.parallelStream()
list.stream().parallel()

Note

Stream实现了AutoCloseable接口,即流在最终操作(terminal operation)后,流会自动关闭,但链式操作不会。

Stream<Integer> s1 = Stream.of(1,2,3,4);
s1 = s1.filter(v -> v % 2 == 0);
s1 = s1.map(v -> v * 2);
System.out.println(s1.reduce(Integer::sum).get());
// 12

jdk1.8新特性:stream流 报错:stream has already been operated upon or closed-CSDN博客

示例

List<A> list = Stream.of(new A("张一", 12), new A("张二", 13), new A("张三", 10)).collect(Collectors.toList());
list.stream().filter(v -> v.age == 12).collect(Collectors.toList());
// filter:[{"age":12,"name":"张一"}]

list.stream().peek(v -> v.age += 1).collect(Collectors.toList());
list.stream().peek(v -> v.age -= 1).collect(Collectors.toList());
// peek:[{"age":13,"name":"张一"},{"age":14,"name":"张二"},{"age":11,"name":"张三"}]
// peek:[{"age":12,"name":"张一"},{"age":13,"name":"张二"},{"age":10,"name":"张三"}]

list.stream().map(v -> v.name).collect(Collectors.toList());
// map:["张一","张二","张三"]
list.stream().map(v -> {
    Map<String, Object> m = new HashMap<>();
    m.put("name", v.name);
    m.put("age", v.age);
    return m;
}).collect(Collectors.toList());
// map:[{"name":"张一","age":12},{"name":"张二","age":13},{"name":"张三","age":10}]

list.stream().map(v -> v.name).collect(Collectors.joining("、"));
// collect:"张一、张二、张三"

list.stream().allMatch(v -> v.age > 10);
// allMatch:false
list.stream().allMatch(v -> v.age >= 10);
// allMatch:true
list.stream().anyMatch(v -> v.age <= 10);
// anyMatch:true

list.stream().findAny().orElse(new A("张四", 15));
// findAny:{"age":12,"name":"张一"}
Stream<A>.empty().findAny().orElse(new A("张四", 15));
// findAny:{"age":15,"name":"张四"}

list.stream().flatMap(v -> new ArrayList<A>() {{add(v);add(new A("张四", 18));}}.stream()).collect(Collectors.toList());
// flatMap:[{"age":12,"name":"张一"},{"age":18,"name":"张四"},{"age":13,"name":"张二"},{"age":18,"name":"张四"},{"age":10,"name":"张三"},{"age":18,"name":"张四"}]

Random r = new Random();
Stream.generate(() -> new A("张四", r.nextInt(29))).limit(6).collect(Collectors.toList());
// generate/limit:[{"age":8,"name":"张四"},{"age":24,"name":"张四"},{"age":8,"name":"张四"},{"age":28,"name":"张四"},{"age":15,"name":"张四"},{"age":21,"name":"张四"}]

list.stream().max((a, b) -> a.age - b.age).get();
list.stream().max(Comparator.comparingInt(a -> a.age)).get();
// max:{"age":13,"name":"张二"}

Stream.of(1, 2, 3).reduce((a, b) -> a + b).get();
Stream.of(1, 2, 3).reduce(Integer::sum).get();
// reduce:6
Stream.of(1, 2, 3).reduce(0, (a, b) -> a + b);
// reduce:6
list.stream().reduce(0, (i, a) -> i + a.age, (a, b) -> a + b);
// reduce:35
list.stream().reduce(new ArrayList<A>(), (i, a) -> {
    i.add(a);
    return i;
}, (i, i1) -> {
    i.addAll(i1);
    return i;
});
// reduce:[{"age":12,"name":"张一"},{"age":13,"name":"张二"},{"age":10,"name":"张三"}]

list.stream().filter(v -> v.age > 10).toArray(A[]::new);
// toArray:{"age":12,"name":"张一"}{"age":13,"name":"张二"}

reduce (聚合)三个重载的解释

Optional<T>	reduce(BinaryOperator<T> accumulator);
T			reduce(T identity, BinaryOperator<T> accumulator);
<U> U		reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
第一重载

使用累加器聚合,由于流可能为空,所以返回Optional<T>

累加器accumulator.apply(T i, T a)中,i为之前计算的累加数,a:当前元素

第二重载

在第一重载的基础上,增加了默认值,避免保证了流为空的情况下没有元素参与聚合,所以返回T

Note

官方文档指出了参数T identity的两个重要使用原则:

  1. 它应该被理解为默认值,而非累加的起始值

此重载等效于:

T result = identity;
for (T element : this stream)
    result = accumulator.apply(result, element)
return result;
  1. 它的值应该被严格规定,对于所有的t(stream种的元素),必须满足 accumulator.apply(identity, t)

综上,T identity会参与聚合计算,但它只应该作为默认值使用,否则并行计算时,结果会出错。例如进行累加计算,那么它的值应该是0

第三重载

在第二重载的基础,默认值identity的类型不限定为流元素T,可自由指定为U,同accumulator.apply第一参数、返回值。例如可以求流元素的某一属性值的和,并使用Integer返回。

第三参数BinaryOperator<U> combiner看似很费解,测试也感觉貌似没有用,事实上,它是对第三重载聚合结果类型自由指定特性在并行计算时的必要补充。官方文档上identity/u/t/accmulator/combiner的关系:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

恒等式具有以下含义:

  • 等式中的u指两个或多个流元素t聚合的结果,简化来说就是u + (identity + t) == u + t,即默认值U identity的使用原则同第二重载
  • 第三参数的意义:第二重载累加器为T accumulator.apply(T, T),即两个t聚合成一个t,并行计算时每个线程都累加产生一个t,聚合成最终结果t时,同样适用这个累加器;第三重载累加器为U accumulator.apply(U, T),即ut聚合成u,并行计算时每个线程都累加产生一个u,聚合成最终结果u时,就需要指定一个U combiner.apply(U, U)的合并器了,只有多线程计算时,这个合并器才会被调用。

distinct 适用于对象

distinct基于equals方法判断重复,Integer/Long/String等实现了equals方法,所以可以被排重。当我们自己的对象需要排重时,也需要在equals方法中实现重复逻辑。