Stream 是 Java 8 的新特性之一,它可以对集合进行非常复杂的查找、过滤、筛选等操作。Stream 也称为“流”,本文参考掘金大佬的文章:Java 8 Stream 从入门到进阶——像SQL一样玩转集合 - 掘金 通过流的定义、特征、创建过程和使用这四方面的内容,来详细了解一下 Stream 的强大。
流的定义
Java 8 引入的“流”的概念可以帮助我们更方便快捷的处理集合,不再依赖于循环遍历去处理集合,这也是 Stream 的强大之处的体现。那么,什么是“流”呢?
官方解释是:A Stream is a sequence of elements from a source.(流是一个来自数据源的元素队列。)
简单来说就是:流是对数据源的包装,它允许我们对数据源进行聚合操作,并且可以方便快捷地进行批量处理。与我们在日常生活中看到的水流在管道中流淌类似,可以理解为 Java 中的“流”也是可以在“管道”中传输的,并且可以在“管道”的节点处进行处理,比如筛选、排序、过滤等。
元素流在管道中经过中间操作(intermediate opertaion)的处理,最后由终端操作(terminal opertaion)得到中间操作处理的结果,就是“流”工作的一个流程,需要注意的是一个“管道”只允许有一个终端操作,也就是说,一个“流”只会有一个终端处理。
中间操作可以分为无状态操作和有状态操作,无状态操作是指当前元素的处理不受此元素之前元素的影响;有状态操作是指该操作只有拿到所有元素才能继续下去。
终端操作也可分为短路操作与非短路操作,短路操作是指遇到符合条件的元素就可以得到最终结果,而非短路操作是指必须处理所有元素才能得到最终结果。
| Stream 操作分类 | ||
|---|---|---|
| 中间操作 | 有状态操作 | distinct(),sorted(),limit(),skip() 等 |
| 无状态操作 | filter(),mapToInt(),mapToLong(),peek(),map() 等 | |
| 终端操作 | 短路操作 | anyMatch(),findFirst(),findAny() 等 |
| 非短路操作 | forEach(),collecct(),max(),min(),count() 等 | |
那么,如何区分中间操作与终端操作呢?一般来说,如果使用的方法有返回 Stream 类型的值,那么就是中间操作,否则就是终端操作。
流的特征
“流”一般有以下几个特征:
1.流并不存储数据,所以它不是一个数据结构,它也不会修改底层的数据源,它是为了函数式编程而生。
2.惰性执行的,例如 filter(),map() 等都是延迟执行的。流的中间操作总是惰性的。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream().filter(element -> {
System.out.println("filter() " + element);
return element.contains("c");
});
}
}
如上代码所示,流中有三个元素,那么正常来说是会依次打印出 "filter() a"、"filter() b"、"filter() c" 的,但是实际上一行也没有输出,因为这个流并没有终端操作。
我们改一下代码,加一个 findFirst() 的终端操作:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class StreamDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> stream = list.stream().filter(element -> {
System.out.println("filter() " + element);
return element.contains("b");
}).findFirst();
System.out.println(stream);
}
}

执行结果显示只调用了两次 filter() 方法,这也是流的中间操作总是惰性执行的特征的体现。
3.流有可能是无限的。虽然集合具有有限的大小,但流不需要。短路操作中,如 limit(n) 或findFirst(),允许在有限的时间内完成对无限流的计算。
4.流还是消耗品。在流的生命周期中,流的元素只被访问一次。与迭代器一样,必须生成新的流来重新访问源的相同元素。被访问过的流会被关闭。
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class StreamDemo {
public static void main(String[] args) {
IntStream intStream = IntStream.of(1, 2, 3);
OptionalInt anyElement = intStream.findAny();
System.out.println(anyElement);
OptionalInt firstElement = intStream.findFirst();
System.out.println(firstElement);
}
}

intStream 已经有过一次 findAny() 的终端操作了,执行完 findAny() 之后 intStream 就被关闭了,所以当第二次再使用 findFirst() 终端操作对 intStream 进行操作时,就会报错。
import java.util.Arrays;
import java.util.Optional;
public class StreamDemo {
public static void main(String[] args) {
Integer[] arrayInt = {1, 2, 3};
Optional<Integer> anyElement = Arrays.stream(arrayInt).findAny();
System.out.println(anyElement);
Optional<Integer> firstElement = Arrays.stream(arrayInt).findFirst();
System.out.println(firstElement);
}
}
这样改造之后就可以多次执行终端操作了。
流的创建
创建一个 Java 流有许多种方式,需要注意的是,流一旦被创建,那么它的数据源是不能被修改的,所以我们可以根据同一个数据源创建多个流。
创建一个空流
import java.util.stream.Stream;
public class StreamDemo {
public static void createStream() {
Stream<String> stringStream = Stream.empty();
System.out.println(stringStream);
}
public static void main(String[] args) {
createStream();
}
}
前面说过了,流中并不存储数据,所以不要尝试去打印流中的内容。
使用数组创建流
public static void createStreamWithArray(String[] strArr) {
Stream<String> stringStream = Arrays.stream(strArr);
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
Stream<String> stringStream1 = Arrays.stream(strArr, 1, 3);
stringStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
我们可以使用数组的全部元素或部分元素来创建流

使用集合创建流
public static void createStreamWithList(List<String> list) {
Stream<String> stringStream = list.stream();
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}

使用 Stream.builder() 创建流
public static void createStreamWithBuilder() {
Stream<String> stringStream = Stream.<String>builder().add("1").add("2").add("3").build();
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}

使用 File 来创建流
public static void createStreamWithFile() throws Exception {
Path path = Paths.get("C:\\Users\\jiaoxian\\Desktop\\Stream.txt");
Stream<String> fileStream = Files.lines(path);
fileStream.forEach(stream -> {
System.out.println(stream);
});
System.out.println();
Path path1 = Paths.get("C:\\Users\\jiaoxian\\Desktop\\Stream1.txt");
Stream<String> fileStream1 = Files.lines(path1, Charset.forName("UTF-8"));
fileStream1.forEach(stream -> {
System.out.println(stream + " ");
});
System.out.println();
}
文件中的每一行内容就是流中的一个元素

使用 Stream.iterate() 创建流
private static void createStreamWithIterate() {
Stream<Integer> integerStream = Stream.iterate(1, n -> n * 2).limit(5);
integerStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
创建一个由首项为 1,公比为 2,项数为 5的等比数列组成的流。

使用Stream.generate() 创建流
private static void createStreamWithGenerate() {
Stream<String> stringStream = Stream.generate(() -> "generate").limit(5);
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
generate() 方法接受一个 Supplier<T> 来生成元素。因为流是无限的,所以我们需要设置流的 size。

使用 range() 创建流
private static void createStreamWithRange() {
IntStream intStream = IntStream.range(1, 4);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
IntStream intStream1 = IntStream.rangeClosed(1, 4);
intStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = LongStream.range(1, 4);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream1 = LongStream.rangeClosed(1, 4);
longStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
Java 中 int、long、double 这三种基本类型都可以用来创建其对应的基本流。但是因为 Stream<T> 是泛型接口,所以无法用基本类型作为类型参数,因此我们可以使用使用IntStream,LongStream,DoubleStream 来创建对应的基本流。
其中 IntStream 和 LongStream 可以使用 range() 和 rangeClosed() 方法来创建,这两个方法的区别就在于 range() 是一个前闭后开的区间,而 rangeClosed() 是一个前闭后闭的区间。

DoubleStream 没有 range() 和 rangeClosed() 方法。
使用 of() 创建流
private static void createStreamWithOf() {
IntStream intStream = IntStream.of(1, 2, 3);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = LongStream.of(1, 2L, 3);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
DoubleStream doubleStream = DoubleStream.of(1, 2, 3.0);
doubleStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
IntStream、LongStream、DoubleStream 都可以使用 of() 方法来创建流。

需要注意的是:为了标准和规范, 创建 LongStream、DoubleStream 流时尽量使用标准的数据类型,即 long 类型的使用 "L" 或 "l" 结尾,double 类型的使用小数点形式。尽管 int 类型的数据也可以去创建 LongStream、DoubleStream 流,但是作为合格的程序员,还是建议一切以标准和规范为准。
使用 Random 类创建流
private static void createStreamWithRandom() {
Random random = new Random();
IntStream intStream = random.ints(3);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = random.longs(4);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
DoubleStream doubleStream = random.doubles(5);
doubleStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
IntStream、LongStream、DoubleStream 都可以使用 Random 类来创建流。

类 CharStream
因为Java 没有 CharStream,所以我们可以使用 IntStream 来替代字符的流,当然也可以使用正则表达式来创建字符流。
private static void createCharStream() {
IntStream charStream = "abcd".chars();
charStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
Stream<String> charStream1 = Pattern.compile(",").splitAsStream("a,b,c");
charStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
创建流的所有代码如下:
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
public class StreamDemo {
public static void createEmptyStream() {
Stream<String> stringStream = Stream.empty();
System.out.println(stringStream);
}
public static void createStreamWithArray(String[] strArr) {
Stream<String> stringStream = Arrays.stream(strArr);
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
Stream<String> stringStream1 = Arrays.stream(strArr, 1, 3);
stringStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
public static void createStreamWithList(List<String> list) {
Stream<String> stringStream = list.stream();
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
public static void createStreamWithBuilder() {
Stream<String> stringStream = Stream.<String>builder().add("1").add("2").add("3").build();
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
public static void createStreamWithFile() throws Exception {
Path path = Paths.get("C:\\Users\\jiaoxian\\Desktop\\Stream.txt");
Stream<String> fileStream = Files.lines(path);
fileStream.forEach(stream -> {
System.out.println(stream);
});
System.out.println();
Path path1 = Paths.get("C:\\Users\\jiaoxian\\Desktop\\Stream1.txt");
Stream<String> fileStream1 = Files.lines(path1, Charset.forName("UTF-8"));
fileStream1.forEach(stream -> {
System.out.println(stream + " ");
});
System.out.println();
}
private static void createStreamWithIterate() {
Stream<Integer> integerStream = Stream.iterate(1, n -> n * 2).limit(5);
integerStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
private static void createStreamWithGenerate() {
Stream<String> stringStream = Stream.generate(() -> "generate").limit(5);
stringStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
private static void createStreamWithRange() {
IntStream intStream = IntStream.range(1, 4);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
IntStream intStream1 = IntStream.rangeClosed(1, 4);
intStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = LongStream.range(1, 4);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream1 = LongStream.rangeClosed(1, 4);
longStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
private static void createStreamWithOf() {
IntStream intStream = IntStream.of(1, 2, 3);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = LongStream.of(1, 2L, 3);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
DoubleStream doubleStream = DoubleStream.of(1, 2, 3.0);
doubleStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
private static void createStreamWithRandom() {
Random random = new Random();
IntStream intStream = random.ints(3);
intStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
LongStream longStream = random.longs(4);
longStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
DoubleStream doubleStream = random.doubles(5);
doubleStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
private static void createCharStream() {
IntStream charStream = "abcd".chars();
charStream.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
Stream<String> charStream1 = Pattern.compile(",").splitAsStream("a,b,c");
charStream1.forEach(stream -> {
System.out.print(stream + " ");
});
System.out.println();
}
public static void main(String[] args) throws Exception {
createEmptyStream();
String[] strArr = {"1", "2", "3", "4", "5"};
createStreamWithArray(strArr);
List<String> list = Arrays.asList(strArr);
createStreamWithList(list);
createStreamWithBuilder();
createStreamWithFile();
createStreamWithIterate();
createStreamWithGenerate();
createStreamWithRange();
createStreamWithOf();
createStreamWithRandom();
createCharStream();
}
}
流的使用
forEach()
forEach() 方法用来对流中的元素进行遍历操作,比如说输出每个元素等。
private static void forEachDemo() {
List<String> strList = new ArrayList<>();
strList.add("A");
strList.add("BC");
strList.add("defg");
strList.stream().forEach(System.out::println);
}
System.out::println 其实就等价于 System.out.println()。前者这种语法形式叫做方法引用(method references),用来替代某些特定形式的 lambda 表达式。如果 lambda 表达式的全部内容就是调用一个已有方法,那么可以用方法引用来替代 lambda 表达式。
filter()
filter() 方法用来对流中的元素进行过滤操作,得到符合条件的元素。
private static void filterDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(5);
intList.stream().filter(stream -> stream % 2 == 1).forEach(System.out::println);
}
找到流中的奇数元素并输出。

distinct()
distinct() 方法用来对流中的元素进行去重处理。
private static void distinctDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(3);
System.out.println("使用 distinct() 前流中的元素为:");
intList.stream().forEach(System.out::println);
System.out.println("使用 distinct() 后流中的元素为:");
intList.stream().distinct().forEach(System.out::println);
}
sorted()
sorted() 用来对流中的元素进行排序操作。
private static void sortedDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(4);
intList.add(3);
intList.add(5);
intList.add(3);
intList.add(2);
System.out.println("使用 sorted() 前流中的元素为:");
intList.stream().forEach(System.out::println);
System.out.println("使用 sorted() 后流中的元素为:");
intList.stream().sorted().forEach(System.out::println);
}

map()
map() 方法对每个元素按照某种操作进行转换,转换后流的元素不会改变,但是元素类型取决于转换之后的类型。
private static void mapDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(3);
intList.stream().map(Integer::doubleValue).forEach(System.out::println);
}
将流中 Integer 类型的元素转换成 Double 类型的。

flatMap()
flat 的英文就是“平坦的”意思,而 flatMap() 方法的作用就是将流的元素摊平,可以理解为将流中的两个元素合并为一个新的元素。
private static void flatMapDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
List<String> strList = new ArrayList<>();
strList.add("A");
strList.add("BC");
Stream.of(intList, strList).flatMap(stream -> stream.stream()).forEach(System.out::println);
}
流中有两个元素,intList 和 strList,使用 flatMap() 方法将两个 list 合并为一个,组成一个新的流元素,并输出。

除了以上这些基本操作外,Stream 还提供了一些规约操作。归约操作(reduction operation)也被称为折叠操作(fold),是通过某种连接动作将所有元素汇总成一个结果的过程。元素求和、求最大值、求最小值、求总数、将所有元素转换成一个集合等都属于归约操作
Stream 类库有两个通用的归约操作 reduce() 和 collect() ,也有一些为简化书写而设计的专用归约操作,比如 sum()、max()、min()、count() 等。
reduce()
reduce() 操作可以实现从一组元素中生成一个值,比如 sum()、max()、min()、count() 等都是reduce() 操作。
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);
第一种形式:
private static void reduceDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
//Optional<Integer> reduceResult = intList.stream().reduce((a, b) -> a + b);
Optional<Integer> reduceResult = intList.stream().reduce(Integer::sum);
System.out.println(reduceResult.get());
}
执行结果为:1 + 2 + 3 = 6;

第二种形式:
private static void reduceDemo2() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
Integer reduceResult = intList.stream().reduce(5, (a, b) -> a + b);
System.out.println(reduceResult);
}
执行结果为:5 +1 + 2 + 3 = 11

第三种形式:
private static void reduceDemo3() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
Integer reduceResult = intList.stream().parallel().reduce(5,
Integer::sum,
Integer::sum);
System.out.println(reduceResult);
}
执行结果为:(5 + 1) + (5 + 2) + (5 + 3) = 21;

这是因为第三种形式中 combiner 参数的作用,它把多个并行结果拼接在了一起。Collection.stream() 和 Collection.parallelStream() 分别产生序列化流(普通流)和并行流。并行(parallel) 和并发(concurrency) 是有区别的。并发是指一个处理器同时处理多个任务,而并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。并发是逻辑上的同时发生,而并行是物理上的同时发生。打个比方:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。并且并行不一定快,尤其在数据量很小的情况下,可能比普通流更慢,只有在大数据量和多核的情况下才考虑并行流。在并行处理情况下,传入给 reduce() 的集合类,需要是线程安全的,否则执行结果会与预期结果不一样。
collect()
collect() 应该算是 Stream 中最重要的一个功能了,而且使用它也是 Java 函数式编程入门一个绝好的途径。
我们先准备一个 Student 的实体类,包含两个参数,String 类型的 name 和 Integer 类型的 score。
public static class Student {
private String name;
private Integer score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public Student(String name, Integer score) {
this.name = name;
this.score = score;
}
}
然后初始化四个 Student 对象,并组成一个 List。
Student student1 = new Student("张三", 90);
Student student2 = new Student("李四", 90);
Student student3 = new Student("王五", 84);
Student student4 = new Student("赵六", 92);
List<Student> studentList = new ArrayList(){{
add(student1);
add(student2);
add(student3);
add(student4);
}};
1.求平均值、求和、分析数据等
private static void collectDemo(List<Student> studentList) {
// 求平均值
Double average = studentList.stream().collect(Collectors.averagingInt(Student::getScore));
System.out.println("平均分为:" + average);
// 求和
Integer sum = studentList.stream().collect(Collectors.summingInt(Student::getScore));
System.out.println("总分为:" + sum);
// 分析数据
IntSummaryStatistics intSummaryStatistics = studentList.stream().collect(Collectors.summarizingInt(Student::getScore));
System.out.println("分析数据为:" + intSummaryStatistics);
}

可从分析数据的结果中获取到 count、max、min 等信息。
2.将流转换成 Collection
提取集合中元素的 name 属性,并且装入字符串类型的集合当中,生成一个新的 List,同时也可将所有的 name 组成一个字符串。
private static void collectDemo2(List<Student> studentList) {
List<String> nameList = studentList.stream().
map(Student::getName).collect(Collectors.toList());
nameList.forEach(System.out::println);
String allName = studentList.stream().
map(Student::getName).
collect(Collectors.joining(",", "[", "]"));
System.out.println(allName);
}
Collectors.joining() 方法用来连接字符串,并且 Collector 会帮我们处理后最后一个元素不应该再加分隔符的问题。

3.将流转换成 Map
Map 不能直接转换成 Stream,但是 Stream 生成 Map 是可行的,在生成 Map 之前,我们应该先定义好 Map 的 Key 和 Value 分别代表什么。
通常在下面三种情况下 collect() 的结果会是Map:
- Collectors.toMap(),使用者需要指定Map的key和value;
- Collectors.groupingBy(),对元素进行group操作;
- Collectors.partitioningBy(),对元素进行二分区操作。
private static void collectDemo3(List<Student> studentList) {
// Collectors.toMap()
System.out.println("Collectors.toMap():");
Map<String, Integer> studentMap = studentList.stream().
collect(Collectors.toMap(Student::getName, Student::getScore));
System.out.println(studentMap);
// Collectors.groupingBy(), 分组
System.out.println();
System.out.println("Collectors.groupingBy():");
Map<Integer, List<Student>> groupByMap = studentList.stream().
collect(Collectors.groupingBy(Student::getScore));
for (Integer key : groupByMap.keySet()) {
String value = groupByMap.get(key).toString();
System.out.println(key + "->" + value);
}
// Collectors.partitioningBy(), 分区
System.out.println();
System.out.println("Collectors.partitioningBy():");
Map<Boolean, List<Student>> partitioningByMap = studentList.stream().
collect(Collectors.partitioningBy(part -> part.getScore() >= 90));
for (Boolean key : partitioningByMap.keySet()) {
String value = partitioningByMap.get(key).toString();
System.out.println(key + "->" + value);
}
System.out.println();
}

关于流的用法的全部代码如下:
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
forEachDemo();
filterDemo();
distinctDemo();
sortedDemo();
mapDemo();
flatMapDemo();
reduceDemo();
reduceDemo2();
reduceDemo3();
Student student1 = new Student("张三", 90);
Student student2 = new Student("李四", 90);
Student student3 = new Student("王五", 84);
Student student4 = new Student("赵六", 92);
List<Student> studentList = new ArrayList(){{
add(student1);
add(student2);
add(student3);
add(student4);
}};
collectDemo(studentList);
collectDemo2(studentList);
collectDemo3(studentList);
}
private static void collectDemo3(List<Student> studentList) {
// Collectors.toMap()
System.out.println("Collectors.toMap():");
Map<String, Integer> studentMap = studentList.stream().
collect(Collectors.toMap(Student::getName, Student::getScore));
System.out.println(studentMap);
// Collectors.groupingBy(), 分组
System.out.println();
System.out.println("Collectors.groupingBy():");
Map<Integer, List<Student>> groupByMap = studentList.stream().
collect(Collectors.groupingBy(Student::getScore));
for (Integer key : groupByMap.keySet()) {
String value = groupByMap.get(key).toString();
System.out.println(key + "->" + value);
}
// Collectors.partitioningBy(), 分区
System.out.println();
System.out.println("Collectors.partitioningBy():");
Map<Boolean, List<Student>> partitioningByMap = studentList.stream().
collect(Collectors.partitioningBy(part -> part.getScore() >= 90));
for (Boolean key : partitioningByMap.keySet()) {
String value = partitioningByMap.get(key).toString();
System.out.println(key + "->" + value);
}
System.out.println();
}
private static void collectDemo2(List<Student> studentList) {
List<String> nameList = studentList.stream().
map(Student::getName).collect(Collectors.toList());
nameList.forEach(System.out::println);
String allName = studentList.stream().
map(Student::getName).
collect(Collectors.joining(",", "[", "]"));
System.out.println(allName);
}
private static void collectDemo(List<Student> studentList) {
// 求平均值
Double average = studentList.stream().collect(Collectors.averagingInt(Student::getScore));
System.out.println("平均分为:" + average);
// 求和
Integer sum = studentList.stream().collect(Collectors.summingInt(Student::getScore));
System.out.println("总分为:" + sum);
// 分析数据
IntSummaryStatistics intSummaryStatistics = studentList.stream().collect(Collectors.summarizingInt(Student::getScore));
System.out.println("分析数据为:" + intSummaryStatistics);
}
private static void reduceDemo3() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
Integer reduceResult = intList.stream().parallel().reduce(5,
Integer::sum,
Integer::sum);
System.out.println(reduceResult);
}
private static void reduceDemo2() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
Integer reduceResult = intList.stream().reduce(5, (a, b) -> a + b);
System.out.println(reduceResult);
}
private static void reduceDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
//Optional<Integer> reduceResult = intList.stream().reduce((a, b) -> a + b);
Optional<Integer> reduceResult = intList.stream().reduce(Integer::sum);
System.out.println(reduceResult.get());
}
private static void flatMapDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
List<String> strList = new ArrayList<>();
strList.add("A");
strList.add("BC");
Stream.of(intList, strList).flatMap(stream -> stream.stream()).forEach(System.out::println);
}
private static void mapDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(3);
intList.stream().map(Integer::doubleValue).forEach(System.out::println);
}
private static void sortedDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(4);
intList.add(3);
intList.add(5);
intList.add(3);
intList.add(2);
System.out.println("使用 sorted() 前流中的元素为:");
intList.stream().forEach(System.out::println);
System.out.println("使用 sorted() 后流中的元素为:");
intList.stream().sorted().forEach(System.out::println);
}
private static void distinctDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(3);
System.out.println("使用 distinct() 前流中的元素为:");
intList.stream().forEach(System.out::println);
System.out.println("使用 distinct() 后流中的元素为:");
intList.stream().distinct().forEach(System.out::println);
}
private static void filterDemo() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);
intList.add(5);
intList.stream().filter(stream -> stream % 2 == 1).forEach(System.out::println);
}
private static void forEachDemo() {
List<String> strList = new ArrayList<>();
strList.add("A");
strList.add("BC");
strList.add("defg");
strList.stream().forEach(System.out::println);
}
public static class Student {
private String name;
private Integer score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public Student(String name, Integer score) {
this.name = name;
this.score = score;
}
}
}





615

被折叠的 条评论
为什么被折叠?



