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 和数组
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream(T array)
- Stream.of()
// 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
- java.io.BufferedReader.lines()
1.4 静态工厂
- java.util.stream.IntStream.range()
- java.nio.file.Files.walk()
- java.nio.file.Files.lines()
1.5 自己构建
- java.util.Spliterator
- Stream.generate
通过 Supplier 实例生成,串行、无序。必须限制大小。
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
类似 reduce
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 其它
- Random.ints()
- BitSet.stream()
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()
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操作
- 对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
- 对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
3 典型用法
3.1 转换结构
- Array
String[] strArray1 = stream.toArray(String[]::new);
- 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));
- String
String str = stream.collect(Collectors.joining()).toString();
3.2 映射
把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素。
- map/flatMap
1:1 映射
List<String> output = wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
- 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 筛选
- 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}。
- findFirst/limit/skip
- min/max/distinct
3.4 遍历
- 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());
}
}
- 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 匹配
- allMatch:Stream 中全部元素符合传入的 predicate,返回 true
- anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
- noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
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 );
}
- onClose 方法
对一个 stream 对象调用 onClose 方法会返回一个在原有功能基础上新增了关闭功能的 stream 对象 - close()方法
当对 stream 对象调用 close() 方法时,与关闭相关的处理器就会执行。