流的创建:
- List, Array, File, IO, ...
// create stream from list
List<Integer> list = List.of(1, 2, 3);
list.stream().filter(i -> i > 2).forEach(System.out::println);
// create stream from array
Integer[] array = new Integer[]{1, 2, 3};
Stream<Integer> arrStream = Arrays.stream(array);
// create stream from Stream.of
Stream<Integer> ofStream = Stream.of(1, 2, 3);
// use stream builder
Stream<Integer> builderStream = Stream.<Integer>builder().add(1).add(2).add(3).build();
// create stream from files
Path path = Paths.get("file.txt");
try {
Stream<String> fileStream = Files.lines(path);
fileStream.forEach(System.out::println);
} catch (IOException e) {
System.err.println("读取文件失败: " + e.getMessage());
}
// create stream from range
IntStream intStream = IntStream.range(1, 10);
System.out.println(intStream.count());
// 无限流创建 infinite qequential stream
Stream.generate(() -> "hello"); // Returns an infinite sequential unordered stream where each element is generated by the provided Supplie
Stream<Double> limit = Stream.generate(Math::random).limit(10);
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);
iterateStream.forEach(System.out::println);
// parallel stream
Stream<Integer> parallelStream = list.parallelStream();
parallelStream.forEach(System.out::println);
中间操作 Intermediate Operations
如 Filter Map Sort ... 每一次中间操作都产生一个Stream流,从而支持链式调用 中间操作是惰性执行的,意味着它们不会立即执行,遇到终端操作才会执行
终端操作 Terminal Operations如 Collect Reduce Count Match ... ForEach 流中的元素会被消费,流就不能在被操作了
不熟悉不常用的流强调一下:
Stream API提供了IntStream, LongStream和DoubleStream来分别处理Int long double类型数据
IntStream intStream = IntStream.range(1, 4);
// 生成1, 2, 3 --左闭右开
Stream<Integer> stream = intStream.boxed();// 转成普通Stream对象
无限流:生成无限元素的流,一般会用limit限制
Stream<Double> limit = Stream.generate(Math::random).limit(10);
Iterate方法:生成数学序列或者是先迭代算法
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);// 生成等差数列
Stream<T> iterate = Stream.iterate(0, n -> n <= 10, n -> n + 2);// 起始元素、筛选条件、生成下一个元素的函数。since java9
中间操作 Intermediate operations对流中的元素进行处理,如筛选、映射、排序等
根据操作的性质分为以下几个类别
- Filtering and Slicing用于过滤或缩减流中的元素数量:filter/distinct/limit/skip...
List<String> list = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
list.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.forEach(System.out::println);
list.stream()
.limit(3)// limit the stream to the first 3 elements
.forEach(System.out::println);
list.stream()
.skip(3)// skip the first 3 elements
.forEach(System.out::println);
- Mapping映射,用于转换流中的元素或提取元素的特定属性:map/flatMap/mapToInt...
List<String> list = List.of("a", "b", "c", "d", "e");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
Map方法适用于单层结构的流,进行元素一对一的转换,例如更改数据类型或提取信息。但对于嵌套的集合、数组或其他多层结构的数据处理不够灵活,在这些情况下flatMap成为更合适的选择。它不仅能够执行map的转换功能,还能够扁平化多层数据结构,将它们转换合并成一个单层流。
过程是这样的:首先它先创建一个流,以下一层结构的数据作为流的元素,然后再将该数据结构元素转换为独立的流,最后将它们合并成一个单层流
List<List<Person>> personGroup = List.of(
List.of(new Person("a", 10), new Person("b", 20)),
List.of(new Person("c", 30), new Person("d", 40)),
List.of(new Person("e", 50), new Person("f", 60))
);
personGroup.stream()
.flatMap(List::stream)
.forEach(System.out::println);
- Sorting排序
Stream.of("apple", "blueberry", "cherry", "orange", "pear")
.sorted()
.forEach(System.out::println);
Stream.of("apple", "blueberry", "cherry", "orange", "pear")
.sorted(Comparator.comparingInt(String::length))
.forEach(System.out::println);
结束操作 Terminal Operations
分为:
Search and match short
- circuiting operations短路操作
- anyMatch
- nonMatch
- allMatch
- findFirst
- findAny
List<Person> list = List.of(new Person("a", 10),
new Person("b", 20),
new Person("c", 30),
new Person("d", 40),
new Person("e", 50));
boolean anyMatch = list.stream().anyMatch(p -> p.age > 30);
boolean noneMatch = list.stream().noneMatch(p -> p.age > 50);
boolean allMatch = list.stream().allMatch(p -> p.age > 10);
System.out.println("anyMatch: " + anyMatch);
System.out.println("noneMatch: " + noneMatch);
System.out.println("allMatch: " + allMatch);
list.stream()
.filter(p -> p.age > 30)
.findFirst() // return a Optional<T> because it may not exist
.ifPresent(System.out::println);
list.stream()
.filter(p -> p.age > 30)
.findAny()
.ifPresent(System.out::println);
Aggregation
- 聚合操作是规约操作Reduction operation的一种特殊形式
- count - 可以直接用Stream,也可以转成IntStream(无必要),返回值是Optional
- max - 可以直接用Stream,也可以转成IntStream,返回值是Optional
- min - 可以直接用Stream,也可以转成IntStream,返回值是Optional
- average - 需要转成基本数据流IntStream,求平均值后返回值是OptionalDouble
- sum - 需要转成基本数据流IntStream,求sum后结果是int
List<Integer> integers = List.of(11, 22, 33, 44, 55);
// can use Stream or IntStream
System.out.println("count: " + integers.stream().count());
System.out.println("count: " + integers.stream().mapToInt(i -> i).count());// use mapToInt instead of map
// map or mapToInt
integers.stream().max(Integer::compareTo).ifPresent(n -> System.out.println("max: " + n));
integers.stream().mapToInt(i -> i).max().ifPresent(n -> System.out.println("max: " + n));
// min or mapToInt
integers.stream().min(Integer::compareTo).ifPresent(n -> System.out.println("min: " + n));
integers.stream().mapToInt(i -> i).min().ifPresent(n -> System.out.println("min: " + n));
// must use mapToInt to get average or sum
integers.stream().mapToInt(i -> i).average().ifPresent(i -> System.out.println("average: " + i));
System.out.println("sum: " + integers.stream().mapToInt(i -> i).sum());
Reduction
- 更为通用,通过一个自定义的累加器函数,对流中的所有元素进行迭代处理,以累计出迭代结果,你可以实现任何类型的结果汇总,不仅是数学上的聚合,而是任何形式的数据合并,比如拼接字符串, 合并集合等。
- reduce
- reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。
List<Integer> list = List.of(1, 2, 3, 4, 5);
list.stream().reduce(0, (a, b) -> a + b);// identity accumulator
list.stream().reduce(Integer::sum).ifPresent(System.out::println);// accumulator
System.out.println(list.stream().reduce(0, Integer::sum));
Collection
- collect
List<Person> list = List.of(new Person("a", 10),
new Person("b", 20),
new Person("c", 30),
new Person("d", 40),
new Person("e", 50));
list.stream()
.filter(p -> p.age > 30)
// .toList()
.collect(Collectors.toList())
.forEach(System.out::println);
list.stream()
.filter(p -> p.age > 30)
.collect(Collectors.toMap(Person::getName, Person::getAge))
.forEach((k, v) -> System.out.println(k + ": " + v));
list.stream()
.collect(Collectors.groupingBy(Person::getName))
.forEach((k, v) -> System.out.println(k + ": " + v));
list.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 18))
.forEach((k, v) -> System.out.println(k + ": " + v));
System.out.println(list.stream()
.map(Person::getName)
.collect(Collectors.joining(",")));
IntSummaryStatistics statistics = list.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println(statistics.getMax());
System.out.println(statistics.getAverage());
System.out.println(statistics.getSum());
Iteration
- forEach
Parallel Stream 并行流
- 核心工作原理:并行流在开始时,spliterator分割迭代器将数据分割成多个片段,分割采用迭代的方式动态进行,然后fork/join框架将数据片段分配到多个线程和处理器核心上进行并行处理,处理完后将数据汇总合并。
List.of("A", "B", "C", "D", "E", "F", "G", "H", "I", "J")
.parallelStream()
.map(String::toLowerCase)
.forEach(System.out::println);// 因为并行流是多线程的,所以结果是乱序的
```
List.of("H", "E", "L", "L", "O", "W", "O", "R", "L", "D")
.parallelStream()
.map(String::toLowerCase)
.forEachOrdered(System.out::print);// 结果是按顺序的