原文链接:Java8 – 06 – Stream类常用方法解析
相关文章:
Java8 – 01 – 行为参数化
Java8 – 02 – 函数式接口
Java8 – 03 – lambda表达式
Java8 – 04 – 引入Stream
Java8 – 05 – Collectors类常用方法解析
Java8 – 06 – Stream类常用方法解析
Java8 – 07 – IntStream类常用方法解析
在了解完 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 +
'}';
}
}
接收一个谓词 (一个返回 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);
去除流中重复元素 (根据流中元素的 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]
截断流,返回一个不超过指定长度的流
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);
跳过元素,返回一个跳过了前 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);
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个新的元素,返回由新元素组成的流
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); // [小白, 小黑, 小红, 小明]
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 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()));
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 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()));
接收一个函数作为参数,将该函数应用到流中每个元素上,并将其映射成一个 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()));
如果让我们来获取学生姓名的汉字,而且不能重复 (也就是获得:小, 白, 黑, 红, 明),这该怎么办呢?
一开始我们会想到使用 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)));
// [小, 白]
// [小, 黑]
// [小, 红]
// [小, 明]
接着我们想到可以将数组转换为流来处理,如下所示
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())));
// [小, 白]
// [小, 黑]
// [小, 红]
// [小, 明]
最后 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 将多个流连接成了一个流
用法与 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()));
用法与 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()));
用法与 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()));
检查谓词是否至少匹配流中一个元素
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
检查谓词是否匹配流中所有元素
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
检查谓词是否不匹配流中所有元素
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
将流归约为单个值,用 Optional 进行包裹
该方法有三个参数
U identity
BiFunction accumulator
BinaryOperator 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
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
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 在单线程情况下,一般会返回第一个元素,而在多线程环境下,则不能确保返回的是第一个元素
循环遍历流中的所有元素 (并行处理)
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 表现为并行处理
按顺序循环遍历流中的所有元素 (串行处理)
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 则表现为串行处理,输出顺序与添加时相同
计算流中元素的个数
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
将流按照指定的比较器筛选出最大元素,用 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());
将流按照指定的比较器筛选出最小元素,用 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());
将流中所有元素按照指定规则进行排序
该方法有一个参数
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);
接收一个 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);
将流转换为 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));
归约操作,将流中所有元素收集到一个集合中
该方法有三个参数
Supplier
BiConsumer
BiConsumer
该方法有两个重载方法,为了方便展示我们将参数类型省去
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]
关闭流
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);
判断流是否为并行流,如果是则为 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
返回并行的等效流
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
返回具有附加关闭处理程序的等效流
即当调用 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
返回流的元素迭代器
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() + " ");
}
// 小白 小黑 小红 小明
返回顺序的等效流
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 + " "));
返回无序的等效流
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 + " "));
返回流的可分割迭代器 (是 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);
根据传入的元素构建顺序流
该方法有两个重载方法
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<Object> empty = Stream.empty();
System.out.println(empty.collect(toList())); // []
合并两个流
如果两个输入流都是串行流的,则得到的结果流是有序的;如果任何一个输入流是并行流,则得到的结果流是无序的
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<Object> builder = Stream.builder();
System.out.println(builder.build().collect(toList())); // []
返回一个无限流,需要截断
该方法有两个参数
final T seed
final UnaryOperator
该方法有一个重载方法,为了方便展示我们将参数类型省去
iterate(seed, f)
Stream.iterate(0, n -> n + 2).limit(5).forEach(System.out::print); // 02468
返回一个无限流,接收一个供给函数作为参数,来提供新值,需要截断
// 0.5393030689484258
// 0.2915922391351988
// 0.8025149159858009
// 0.7681873637828662
// 0.8158901245174043
Stream.generate(Math::random).limit(5).forEach(System.out::println);