Stream

Wu Jun 2020-01-03 04:43:49
Categories: > > Tags:

Java 8 中的 Stream 是对集合对象功能的增强,像一个高级版本的Iterator,专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。

Stream 单向,不可往复,数据源本身可以是无限的。
所有 Stream 的操作必须以 lambda 表达式为参数。

1 Stream 的构造

1.1 数值型

对于基本数值型,特别提供了三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。

IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);

1.2 Collection 和数组

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();

1.3 BufferedReader

1.4 静态工厂

1.5 自己构建

Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//Another way
IntStream.generate(() -> (int) (System.nanoTime() % 100)).
limit(10).forEach(System.out::println);
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));
//0 3 6 9 12 15 18 21 24 27

1.6 其它

2 操作类型

2.1 转换操作(Intermediate)

一个流可以后面跟随零个或多转换操作。打开流,操作数据,返回一个新的流。这类操作都是惰性化的(lazy),仅仅调用到,并没有真正开始流的遍历。

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

2.2 最终操作(Terminal)

一个流只能有一个最终操作,必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

遍历次数

Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

2.3 short-circuiting操作

anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

3 典型用法

3.1 转换结构

  1. Array
String[] strArray1 = stream.toArray(String[]::new);
  1. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));

Set set1 = stream.collect(Collectors.toSet());

Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));

Map<String, Long> map = list.stream()
                    .collect(Collectors.toMap(Dto::getId, Dto::getNum, (oldValue, newValue) -> oldValue));

Map<String, Object> map = list.stream()
                    .collect(Collectors.toMap(Dto::getId, Function.identity(), (oldValue, newValue) -> oldValue));
  1. String
String str = stream.collect(Collectors.joining()).toString();

3.2 映射

把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素。

  1. map/flatMap
    1:1 映射
List<String> output = wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
  1. flatMap
    1:n 映射
Stream<List<Integer>> inputStream = Stream.of(
 Arrays.asList(1),
 Arrays.asList(2, 3),
 Arrays.asList(4, 5, 6)
 );
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());//1,2,3,4,5,6

3.3 筛选

  1. filter
    filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =
Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
//经过条件“被 2 整除”的 filter,剩下的数字为 {2, 4, 6}。
  1. findFirst/limit/skip
  2. min/max/distinct

3.4 遍历

  1. forEach
    terminal 操作。
    forEach 不能修改自己包含的本地变量值,也不能用 break/return 之类的关键字提前结束循环。
// Java 8
roster.stream()
 .filter(p -> p.getGender() == Person.Sex.MALE)
 .forEach(p -> System.out.println(p.getName()));
// Pre-Java 8
for (Person p : roster) {
 if (p.getGender() == Person.Sex.MALE) {
 System.out.println(p.getName());
 }
}
  1. peek
    intermediate 操作
Stream.of("one", "two", "three", "four")
 .filter(e -> e.length() > 3)
 .peek(e -> System.out.println("Filtered value: " + e))
 .map(String::toUpperCase)
 .peek(e -> System.out.println("Mapped value: " + e))
 .collect(Collectors.toList());

3.5 排序

sorted

List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());

3.6 reduce

// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").
 filter(x -> x.compareTo("Z") > 0).
 reduce("", String::concat);

3.7 匹配

3.8 分组

Collectors 类,groupingBy

final Map< Status, List< Task > > map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
//{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

3.9 归组

Collectors 类,partitioningBy

Map<Boolean, List<Person>> children = Stream.generate(new PersonSupplier()).
 limit(100).
 collect(Collectors.partitioningBy(p -> p.getAge() < 18));
System.out.println("Children number: " + children.get(true).size());//Children number: 23 
System.out.println("Adult number: " + children.get(false).size());//Adult number: 77

3.10 I/O操作

Stream API 不仅仅处理 Java 集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}