1. 引言

1.1 应用

  • 对集合(Collection)对象功能的增强,提供串行和并行两种模式进行聚合操作(aggregate operation);
  • 借助 Lambda 进行大批量数据操作(bulk data operation) ;
  • 补充:

    • 并行模式通过 fork/join 框架实现, 即 Stream API 实现了高性能的并发程序的封装;
    • import:java.util.stream;

      1.2 特性

  • Stream 非集合元素,非数据结构,用操作管道从 source(Collection、数组、generator function、IO channel)抓取数据且数据量不限;

  • 所有 Stream 的操作必须以 lambda 表达式为参数;
  • 不支持索引访问;
  • 容易实现向其他数据结构,如 List、Set、Array 等的转换;
  • 高级版本的 Iterator:
    • 单向,不可往复,数据只能遍历一次;
    • Iterator 只能显示地通过命令式执行串行化操作,而 Stream 可并行化地隐式执行数据转换和计算;
  • 总结:便利、简洁、高效、强大;

    2. 流的生成 Stream Source

    2.1 方式

  • Collection 和 数组

    • Collection.stream()
    • Collection.parallelStream()
    • Arrays.stream(T array)
  • BufferedReader
    • java.io.BufferedReader.lines()
  • 通过 Stream 接口的静态工厂方法

    • java.util.stream.IntStream.range()
    • java.nio.file.Files.walk()
      • Stream.of() : 有两个 overload 方法,一个接受变长参数,一个接口单一值
      • Stream.generate(Math::random) : 生成一个无限长度的Stream,其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象);
      • Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println) : 生成无限长度的Stream,和generator 不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环;
  • 自定义构建 java.util.Spliterator

  • 其它

    • Random.ints()
    • BitSet.stream()
    • Pattern.splitAsStream(java.lang.CharSequence)
    • JarFile.stream()

      2.2 Stream 的三种包装类

  • 为三种基本数值型提供了对应的 Stream:IntStream、LongStream、DoubleStream;
    补充:也可用 Stream、Stream >、Stream,但 boxing 和 unboxing 会很耗时;

    3. 流的使用

    3.1 使用流的一般步骤

  • 获取一个数据源(source)→ 数据转换 → 执行操作获取目标结果;

  • 补充:每次转换原有 Stream 对象不改变,返回一个新的 Stream ;

Stream 学习 - 图1 Stream 学习 - 图2

3.2 Stream 与其它数据结构的转换

  • Array
    String[] strArray1 = stream.toArray(String[]::new);
  • Collection
    List list1 = stream.collect(Collectors.toList());
    List list2 = stream.collect(Collectors.toCollection(ArrayList::new));
    Set set1 = stream.collect(Collectors.toSet());
    Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
  • String
    String str = stream.collect(Collectors.joining()).toString();

    3.3 Stream 的操作

  • 流的操作类型

    • Intermediate:一个流后可有零个或多个 intermediate 操作;
      • 目的:打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用;
      • 具有惰性化(lazy),仅实现方法调用,并未真正开始流的遍历;
      • 包括方法:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered;
      • 补充:flatMap 和 map 类似,不同的是其每个元素转换得到的是 Stream 对象,会把子 Stream 中的元素压缩到父集合中;
    • Terminal:一个流只能有一个 terminal 操作,为流的最后一个操作;
      • 真正开始流的遍历,并且会生成一个结果(eg:count方法会有一个统计结果),或者一个 side effect(eg:forEach);
      • 包括方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
    • short-circuiting
      • 对于一个 intermediate 操作,若接受的是一个无限大(infinite/unbounded)的 Stream,可返回一个有限的新 Stream;
      • 对于一个 terminal 操作,若接受的是一个无限大的 Stream,可在有限时间计算出结果;
      • 当操作一个无限大的 Stream 且需在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件;
      • 包括方法:anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
    • 补充: 一个 Stream 的多次转换操作 (Intermediate 操作) 只会在 Terminal 操作的时候融合起来,一次循环完成,可理解为 Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
  • 操作实现

    3.3.1 通过数组创建Stream

    1. String[] arr_v = {"aa","bbb","cccc","dd"};
    2. //方式一:
    3. Stream<String> stringStream = Arrays.stream(arr_v);// 获取指定范围的 stream : Arrays.stream(arr_v, 1,3)
    4. //也有针对具体元素类型的 InStream 与 DoubleStream
    5. System.out.println(stringStream.count());// Output:4,统计数组的个数
    6. //方式二:
    7. Stream<String> stringStream01 = Stream.of(arr_v);

    3.3.2 count 统计列表中某元素出现的个数

    1. ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    2. list_stream.stream().filter(s->s.equals(6)).count();//outpput:3

    3.3.3 filter 列表对筛选元素进行操作并转化为集合存储

    1. list_stream.stream().filter(s->!s.equals(6)).map(s->s*s).collect(Collectors.toList())
    2. //output:[1, 4, 9, 16, 25]

    3.3.4 sum 列表求和(reduce 好在可以设定初值, collector.summarize 优势在输出结果非常丰富)

    1. ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    2. list_stream.stream().reduce(2,(a,b)->a+2*b);// [Output : 68]初始 sum = 2,对每个元素乘以2再求和
    3. list_stream.stream().collect(Collectors.summarizingInt(a->2*a));// IntSummaryStatistics{count=8, sum=66, min=2, average=8.250000, max=12}

    3.3.5 average 列表求平均数

    1. list_stream.stream().collect(Collectors.averagingInt(i->i))

    3.3.6 max 求元素的最大数

    1. List<Double> list_max = new Random().doubles(1,10).limit(5).boxed().collect(Collectors.toList());// 生成 【1,10】的 5 个随机数
    2. //方式一:
    3. list_max.stream().collect(Collectors.maxBy(Comparator.comparingDouble(i->i*3)));
    4. //方式二:
    5. list_max.stream().reduce(Math::max);

    3.3.7 connect 字符串拼接

    1. ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    2. list_stream.stream().map(i->i.toString()).collect(Collectors.joining("#"));// 1#2#3#4#5#6#6#6
    3. list_stream.stream().map(i->i.toString()).collect(Collectors.joining(",","[","]"));//[1,2,3,4,5,6,6,6]
    4. list_stream.stream().map(i->i.toString()).reduce("#", String::concat);//#12345666
    5. list_stream.stream().map(Objects::toString).reduce("Start", String::concat);//Start12345666
    6. list_stream.stream().map(Objects::toString).reduce(new StringJoiner(",","[","]"),StringJoiner::add,StringJoiner::merge));//[1,2,3,4,5,6,6,6]

    3.3.8 groupby 分组

    1. ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    2. System.out.println(list_stream.stream().collect(groupingBy(i->i>3)));
    3. //{false=[1, 2, 3], true=[4, 5, 6, 6, 6]}

    3.3.9 map 根据 list 创建 map

    1. //获取 2-10 范围内数字的平方
    2. List<Integer> list1 = IntStream.range(2,10).boxed().collect(Collectors.toList());
    3. Map<Integer, Integer> map1 = list1.stream().collect(Collectors.toMap(p->p,q->q*2));// {2=4, 3=6, 4=8, 5=10, 6=12, 7=14, 8=16, 9=18}
    4. //统计每个字符串元素的长度
    5. System.out.println(Stream.of("cyt","ly","hkp","lrx","zjn").map(String::length).collect(Collectors.toList()));

    3.3.10 flapMap

    ```java // 案例一: Map> map = new LinkedHashMap<>(); map.put(“a”, Arrays.asList(1,2,3)); map.put(“b”, Arrays.asList(4,5,6)); System.out.println(map.values().stream().flatMap(List::stream).collect(Collectors.toList())); // output: [1, 2, 3, 4, 5, 6]

// 案例二: List> list_flap = new ArrayList<>(); Map map_flap1 = new HashMap<>(); Map map_flap2 = new HashMap<>(); map_flap1.put(“1”,”ae”); map_flap1.put(“2”,”bd”); map_flap2.put(“3”,”ccc”); map_flap2.put(“4”,”dd”); map_flap2.put(“5”,”dd”); list_flap.add(map_flap1); list_flap.add(map_flap2); Set output = list_flap.stream().map(Map::values).flatMap(Collection::stream).collect(Collectors.toSet()); System.out.println(output);//output: [dd, bd, ccc, ae]

<a name="eXSwt"></a>
#### 3.3.11 peak
```java
Stream.of("one","two","three","four").filter(v->v.length()>3).peek(v->System.out.println("the filtered value: "+v)).
       map(String::toUpperCase).peek(v->System.out.println("the mappped value: "+v)).collect(Collectors.toList());
//output: the filtered value: three, the mappped value: THREE, the filtered value: four,   the mappped value: FOUR

3.3.12 重用 a stream chain 的中间操作

Supplier<Stream<String>> streamSupplier = ()->Stream.of("cyt","ly","hkp","lrx","zjn").map(String::toUpperCase).sorted();
streamSupplier.get().filter(s->s.startsWith("C")).forEach(System.out::println); // output: CYT
System.out.println(streamSupplier.get().collect(Collectors.joining(",","*","*")));// output : *CYT,HKP,LRX,LY,ZJN*

3.3.13 limit、skip 等

  • limit 返回 stream 的前 n 个元素, skip 扔掉前 n 个元素

    ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    System.out.println(list_stream.stream().limit(5).skip(2).collect(Collectors.toList()));//output: [3,4,5]
    

    3.3.14 排序

  • Stream 的排序比数组排序强大之处在于可以对 stream 先进行各类 map、filter、limit、skip 等

    Stream.of("cyt","ly","hkp","lrx","zjn").limit(4).sorted((p1,p2)->p1.compareTo(p2)).forEach(System.out::println);// output: cyt、hkp、lrx、ly
    

    3.3.15 distinct

    ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    System.out.println(list_stream.stream().distinct().collect(Collectors.toList())); // output : [1, 2, 3, 4, 5, 6]
    

    3.3.16 Match

    ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
    System.out.println(list_stream.stream().allMatch(p->p < 6));//output: false
    System.out.println(list_stream.stream().anyMatch(p->p == 1));//output: true
    System.out.println(list_stream.stream().noneMatch(p->p == 0));//output: true
    

    3.4 返回值 optional

    public static void testOptional(){
      // Stream 中的 findAny、max/min、reduce 等方法返回的均是 optional 值
      String str1 = "cyt";
      String str2=null;
      Optional.ofNullable(str1).ifPresent(System.out::println);
      Optional.ofNullable(str2).ifPresent(System.out::println);// optional 避免空指针异常
    
      Stream.of(1,2,3).reduce(Integer::sum).ifPresent(System.out::println);//output: 6
      System.out.println(Stream.of("a","b","c","d").reduce("*",String::concat));
      System.out.println(Optional.ofNullable(str1).map(String::length).orElse(-1));//output: 3
      System.out.println(Optional.ofNullable(str2).map(String::length).orElse(-1));//output: -1
    
      ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
      System.out.println(list_stream.stream().allMatch(p->p < 6));
      System.out.println(list_stream.stream().noneMatch(p->p == 0));//output: true
    }
    

参考文献