相关文章:
在了解完 Stream 的定义以及 Collectors 的常用方法之后,我们再来看看 Stream 类的常用方法
-
Student.java
public class Student { private String name; private int age; private double score; public Student(String name, int age, int score){ this.name = name; this.age = age; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } }
一、filter()
-
接收一个谓词 (一个返回 boolean 的函数) 作为参数,返回一个包括所有符合谓词的元素流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Student> filter = studentList.stream() .filter(student -> student.getAge() > 21).collect(toList()); // [Student(name=小红, age=22, score=80.0), Student(name=小明, age=22, score=82.0)] System.out.println(filter);
二、distinct()
-
去除流中重复元素 (根据流中元素的 hashCode() 和 equals() 方法来实现)
List<Integer> list = Arrays.asList(1, 2, 3, 5, 1, 2, 4); List<Integer> distinct = list.stream().distinct().collect(toList()); System.out.println(distinct); // [1, 2, 3, 5, 4]
三、limit()
-
截断流,返回一个不超过指定长度的流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Student> limit = studentList.stream().limit(1).collect(toList()); // [Student(name=小白, age=20, score=90.0)] System.out.println(limit);
四、skip()
-
跳过元素,返回一个跳过了前 n 个元素的流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Student> skip = studentList.stream().skip(3).collect(toList()); // [Student(name=小明, age=22, score=82.0)] System.out.println(skip);
五、map()
-
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个新的元素,返回由新元素组成的流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<String> studentNameList = studentList.stream().map(Student::getName).collect(toList()); System.out.println(studentNameList); // [小白, 小黑, 小红, 小明]
六、mapToDouble()
-
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 double 元素,返回 double 数值流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); DoubleStream doubleStream = studentList.stream().mapToDouble(Student::getScore); // [90.0, 95.0, 80.0, 82.0] System.out.println(doubleStream.boxed().collect(toList()));
七、mapToInt()
-
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 int 元素,返回 int 数值流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); IntStream intStream = studentList.stream().mapToInt(Student::getAge); // [20, 21, 22, 22] System.out.println(intStream.boxed().collect(toList()));
八、mapToLong()
-
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 long 元素,返回 long 数值流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); LongStream longStream = studentList.stream().mapToLong(Student::getAge); // [20, 21, 22, 22] System.out.println(longStream.boxed().collect(toList()));
九、flatMap()
-
如果让我们来获取学生姓名的汉字,而且不能重复 (也就是获得:小, 白, 黑, 红, 明),这该怎么办呢?
-
一开始我们会想到使用 map,如下所示
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<String[]> studentDistinctNameList = studentList.stream() .map(student -> student.getName().split("")).distinct().collect(toList()); studentDistinctNameList.forEach(data -> System.out.println(Arrays.toString(data))); // [小, 白] // [小, 黑] // [小, 红] // [小, 明]
- 不过这样不行,得到的是一个类型为 String[] 的集合,与我们预期的不符
-
接着我们想到可以将数组转换为流来处理,如下所示
List<Stream<String>> studentDistinctNameList = studentList.stream() .map(student -> student.getName().split("")).map(Arrays::stream).distinct().collect(toList()); studentDistinctNameList.forEach(stream -> System.out.println(stream.collect(toList()))); // [小, 白] // [小, 黑] // [小, 红] // [小, 明]
- 这样也不行,得到的是一个类型为 Stream<String> 的集合,与我们预期的不符
-
最后 flatMap 终于要登场了
List<String> studentDistinctNameList = studentList.stream() .map(student -> student.getName().split("")) .flatMap(Arrays::stream).distinct().collect(toList()); // [小, 白, 黑, 红, 明] System.out.println(studentDistinctNameList);
-
现在终于得到与预期相符的结果了,使用 flatMap 的效果是:各个数组并不是各自映射成一个流,而是映射成流的内容,所有使用
map(Arrays::stream)
方法生成的单个流都被合并到了一个流中,即扁平化为了一个流 -
换句话说就是 flatMap 将多个流连接成了一个流
-
十、flatMapToDouble()
-
用法与 flatMap 类似,返回一个 double 数值流
double[][] data = {{1, 2},{3, 4},{5, 6}}; DoubleStream doubleStream = Arrays.stream(data).flatMapToDouble(Arrays::stream); // [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] System.out.println(doubleStream.boxed().collect(toList()));
十一、flatMapToInt()
-
用法与 flatMap 类似,返回一个 int 数值流
int[][] data = {{1, 2},{3, 4},{5, 6}}; IntStream intStream = Arrays.stream(data).flatMapToInt(Arrays::stream); // [1, 2, 3, 4, 5, 6] System.out.println(intStream.boxed().collect(toList()));
十二、flatMapToLong()
-
用法与 flatMap 类似,返回一个 long 数值流
long[][] data = {{1, 2},{3, 4},{5, 6}}; LongStream longStream = Arrays.stream(data).flatMapToLong(Arrays::stream); // [1, 2, 3, 4, 5, 6] System.out.println(longStream.boxed().collect(toList()));
十三、anyMatch()
-
检查谓词是否至少匹配流中一个元素
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); boolean anyMatch = studentList.stream().anyMatch(student -> student.getScore() == 95); System.out.println(anyMatch); // ture
- 所有学生中只有一个分数为 95,故为 true
十四、allMatch()
-
检查谓词是否匹配流中所有元素
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); boolean allMatch = studentList.stream().allMatch(student -> student.getScore() > 0); System.out.println(allMatch); // true
- 所有学生分数均大于 0,故为 true
十五、noneMatch()
-
检查谓词是否不匹配流中所有元素
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); boolean noneMatch = studentList.stream().noneMatch(student -> student.getScore() == 100); System.out.println(noneMatch); // true
- 所有学生中没有一个分数为 100,故为 true
十六、reduce()
-
将流归约为单个值,用 Optional 进行包裹
-
该方法有三个参数
-
U identity
- 初始值
-
BiFunction<U,? super T,U> accumulator
- 累加器,将两个元素结合起来生成一个新值
-
BinaryOperator<U> combiner
- 组合器,将每个线程产生的结果结合起来生成一个新值,只有使用并行流时有用
-
-
该方法有三个重载方法,为了方便展示我们将参数类型省去
-
reduce(accumulator)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Optional<Double> reduceOptional = studentList.stream() .map(Student::getScore).reduce((x, y) -> x + y); System.out.println(reduceOptional.get()); // 347.0
- 此处我们对学生分数进行归约累加
-
reduce(identity, accumulator)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Double reduce = studentList.stream() .map(Student::getScore).reduce(3.0, (x, y) -> x + y); System.out.println(reduce); // 350.0
- 此处我们以 3.0 作为初始值,然后再对学生分数进行规约累加
-
reduce(identity, accumulator, combiner)
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); Integer reduce = list.parallelStream().reduce(3, (x, y) -> { System.out.println("【" + x + "】 + 【" + y + "】 = 【" + (x + y) + "】"); return x + y; }, (x, y) -> { System.out.println("【" + x + "】 + 【" + y + "】 = 【" + (x + y) + "】"); return x + y; }); // 【3】 + 【4】 = 【7】 // 【3】 + 【5】 = 【8】 // 【3】 + 【3】 = 【6】 // 【3】 + 【1】 = 【4】 // 【3】 + 【2】 = 【5】 // 【7】 + 【8】 = 【15】 // 【4】 + 【5】 = 【9】 // 【6】 + 【15】 = 【21】 // 【9】 + 【21】 = 【30】 System.out.println(reduce); // 30
- 通过输出我们可以很清晰地看到,当使用了并行流 parallelStream 后,会先使用初始值与流中的每个元素进行相加 (应用了第二个参数),然后将得到的结果再相加 (应用了第三个参数),从而得到最终的结果
-
十七、findFirst()、findAny()
-
findFirst()
- 返回当前流中的第一个元素
-
findAny()
- 返回当前流中的任意一个元素
-
对比说明
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Optional<Student> serial1 = studentList.stream().findFirst(); System.out.println(serial1); // Optional[Student(name=小白, age=20, score=90.0)] Optional<Student> serial2 = studentList.stream().findAny(); System.out.println(serial2); // Optional[Student(name=小白, age=20, score=90.0)] Optional<Student> parallel1 = studentList.parallelStream().findFirst(); System.out.println(parallel1); // Optional[Student(name=小白, age=20, score=90.0)] Optional<Student> parallel2 = studentList.parallelStream().findAny(); System.out.println(parallel2); // Optional[Student(name=小红, age=22, score=80.0)]
-
当两者在串行流中,返回的都是第一个元素
-
当两者在并行流中,findFirst 返回的仍然是第一个元素;findAny 在单线程情况下,一般会返回第一个元素,而在多线程环境下,则不能确保返回的是第一个元素
-
十八、forEach()
-
循环遍历流中的所有元素 (并行处理)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); // 小白 小黑 小红 小明 studentList.stream().map(Student::getName) .forEach(name -> System.out.print(name + " ")); System.out.println(); // 小红 小明 小黑 小白 studentList.parallelStream().map(Student::getName) .forEach(name -> System.out.print(name + " "));
-
在串行流中,forEach 表现为串行处理
-
在并行流中,forEach 表现为并行处理
-
十九、forEachOrdered()
-
按顺序循环遍历流中的所有元素 (串行处理)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); // 小红 小明 小白 小黑 studentList.parallelStream().map(Student::getName) .forEach(name -> System.out.print(name + " ")); System.out.println(); // 小白 小黑 小红 小明 studentList.parallelStream().map(Student::getName) .forEachOrdered(name -> System.out.print(name + " "));
-
在并行流中,forEach 表现为并行处理,输出顺序与添加时不同
-
forEachOrdered 则表现为串行处理,输出顺序与添加时相同
-
二十、count()
-
计算流中元素的个数
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); long count = studentList.stream().map(Student::getName).count(); System.out.println(count); // 4
二十一、max()
-
将流按照指定的比较器筛选出最大元素,用 Optional 进行包裹
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Optional<Student> studentMaxOptional = studentList.stream() .max(comparingDouble(Student::getScore)); // Student(name=小黑, age=21, score=95.0) System.out.println(studentMaxOptional.get());
二十二、min()
-
将流按照指定的比较器筛选出最小元素,用 Optional 进行包裹
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Optional<Student> studentMinOptional = studentList.stream() .min(comparingDouble(Student::getScore)); // Student(name=小红, age=22, score=80.0) System.out.println(studentMinOptional.get());
二十三、sorted()
-
将流中所有元素按照指定规则进行排序
-
该方法有一个参数
-
Comparator<? super T> comparator
- 比较器
-
-
该方法有三个重载方法,为了方便展示我们将参数类型省去
-
sorted()
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Double> sorted = studentList.stream() .map(Student::getScore).sorted().collect(toList()); // [80.0, 82.0, 90.0, 95.0] System.out.println(sorted);
- 不加参数默认为升序排序
-
sorted(comparator)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Double> sorted = studentList.stream() .map(Student::getScore).sorted(reverseOrder()).collect(toList()); // [95.0, 90.0, 82.0, 80.0] System.out.println(sorted);
-
二十四、peek()
-
接收一个 Consumer 函数作为参数,并将该函数应用到流中每个元素上
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<Student> peek = studentList.stream().peek(student -> student.setScore(100)).collect(toList()); // [Student(name=小白, age=20, score=100.0), Student(name=小黑, age=21, score=100.0), // Student(name=小红, age=22, score=100.0), Student(name=小明, age=22, score=100.0)] System.out.println(peek);
- 此处我们将所有学生的分数都设置为 100
二十五、toArray()
-
将流转换为 Object 类型的数组
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Object[] objects = studentList.stream().toArray(); // [Student(name=小白, age=20, score=90.0), Student(name=小黑, age=21, score=95.0), // Student(name=小红, age=22, score=80.0), Student(name=小明, age=22, score=82.0)] System.out.println(Arrays.toString(objects));
二十六、collect()
-
归约操作,将流中所有元素收集到一个集合中
-
该方法有三个参数
-
Supplier<R> supplier
- 供给函数,返回一个空的容器,用于存放数据
-
BiConsumer<R, ? super T> accumulator
- 累加器,将元素添加到空容器中
-
BiConsumer<R, R> combiner
- 组合器,将每个线程产生的结果相结合,只有使用并行流时有用
-
-
该方法有两个重载方法,为了方便展示我们将参数类型省去
-
collect(collector)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); List<String> studentNameList = studentList.stream() .map(Student::getName).collect(toList()); // [小白, 小黑, 小红, 小明] System.out.println(studentNameList);
-
collect(supplier, accumulator, combiner)
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); LinkedList<Object> collectList = intList.parallelStream() .collect(LinkedList::new, (list, i) -> { System.out.println("【" + i + "】 + 【2】 = 【" + (i + 2) + "】"); list.add(i + 2); }, (list1, list2) -> { List<Object> cache = new ArrayList<>(list1); list1.addAll(list2); System.out.println("【" + cache + "】.addAll【" + list2 + "】 = 【"+list1+"】"); }); // 【3】 + 【2】 = 【5】 // 【1】 + 【2】 = 【3】 // 【5】 + 【2】 = 【7】 // 【2】 + 【2】 = 【4】 // 【4】 + 【2】 = 【6】 // 【[3]】.addAll【[4]】 = 【[3, 4]】 // 【[6]】.addAll【[7]】 = 【[6, 7]】 // 【[5]】.addAll【[6, 7]】 = 【[5, 6, 7]】 // 【[3, 4]】.addAll【[5, 6, 7]】 = 【[3, 4, 5, 6, 7]】 System.out.println(collectList); // [3, 4, 5, 6, 7]
- 通过输出我们可以很清晰地看到,当使用了并行流 parallelStream 后,会先使用一个空的集合去添加元素 (应用了第二个参数),然后将得到的结果在添加到一起 (应用了第三个参数),从而得到最终的结果
-
二十七、close()
-
关闭流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Stream<Student> stream = studentList.stream(); stream.close(); stream.forEach(System.out::println);
-
同个流只能被消费一次,当流被关闭后再使用流会报错
-
二十八、isParallel()
-
判断流是否为并行流,如果是则为 true,不是则为 false
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); System.out.println(studentList.stream().isParallel()); // false System.out.println(studentList.parallelStream().isParallel()); // true
二十九、parallel()
-
返回并行的等效流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); System.out.println(studentList.stream().parallel().isParallel()); // true
三十、onClose()
-
返回具有附加关闭处理程序的等效流
-
即当调用 close() 方法关闭流后会自动执行 onClose() 方法中添加的操作
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Stream<Student> stream = studentList.stream() .onClose(() -> System.out.println("Hello World")); stream.close(); // Hello World
三十一、iterator()
-
返回流的元素迭代器
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Iterator<String> iterator = studentList.stream().map(Student::getName).iterator(); while (iterator.hasNext()) { System.out.print(iterator.next() + " "); } // 小白 小黑 小红 小明
三十二、sequential()
-
返回顺序的等效流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); // 小红 小明 小白 小黑 studentList.parallelStream().map(Student::getName) .forEach(name -> System.out.print(name + " ")); System.out.println(); // 小白 小黑 小红 小明 studentList.parallelStream().map(Student::getName). sequential().forEach(name -> System.out.print(name + " "));
三十三、unordered()
-
返回无序的等效流
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); // 小红 小明 小黑 小白 studentList.parallelStream().map(Student::getName) .forEach(name -> System.out.print(name + " ")); System.out.println(); // 小红 小白 小明 小黑 studentList.parallelStream().map(Student::getName). unordered().forEach(name -> System.out.print(name + " "));
三十四、spliterator()
-
返回流的可分割迭代器 (是 Java 为了并行遍历数据源中的元素而设计的迭代器)
List<Student> studentList = new ArrayList<>(Arrays.asList( new Student("小白", 20, 90), new Student("小黑", 21, 95), new Student("小红", 22, 80), new Student("小明", 22, 82))); Spliterator<Student> spliterator = studentList.stream().spliterator(); // Student(name=小白, age=20, score=90.0) // Student(name=小黑, age=21, score=95.0) // Student(name=小红, age=22, score=80.0) // Student(name=小明, age=22, score=82.0) spliterator.forEachRemaining(System.out::println);
三十五、Stream.of()
-
根据传入的元素构建顺序流
-
该方法有两个重载方法
-
of(T t)
-
返回包含单个元素的顺序流
Stream.of(1).forEach(System.out::print); // 1
-
-
of(T… values)
-
返回包含多个元素的顺序流
Stream.of(1, 2, 3).forEach(System.out::print); // 123
-
-
三十六、Stream.empty()
-
返回一个空的顺序流
Stream<Object> empty = Stream.empty(); System.out.println(empty.collect(toList())); // []
三十七、Stream.concat()
-
合并两个流
-
如果两个输入流都是串行流的,则得到的结果流是有序的;如果任何一个输入流是并行流,则得到的结果流是无序的
List<Integer> list1 = Arrays.asList(1, 2, 3); List<Integer> list2 = Arrays.asList(4, 5, 6); // 123456 Stream.concat(list1.stream(), list2.stream()).forEach(System.out::print); System.out.println(); // 465123 Stream.concat(list1.stream(), list2.parallelStream()).forEach(System.out::print);
三十八、Stream.builder()
-
返回流的构造器
Stream.Builder<Object> builder = Stream.builder(); System.out.println(builder.build().collect(toList())); // []
三十九、Stream.iterate()
-
返回一个无限流,需要截断
-
该方法有两个参数
-
final T seed
- 初始元素
-
final UnaryOperator<T> f
- 累加器,将该函数应用于上一个元素来生成新元素
-
-
该方法有一个重载方法,为了方便展示我们将参数类型省去
-
iterate(seed, f)
Stream.iterate(0, n -> n + 2).limit(5).forEach(System.out::print); // 02468
-
四十、Stream.generate()
-
返回一个无限流,接收一个供给函数作为参数,来提供新值,需要截断
// 0.5393030689484258 // 0.2915922391351988 // 0.8025149159858009 // 0.7681873637828662 // 0.8158901245174043 Stream.generate(Math::random).limit(5).forEach(System.out::println);