Stream API
1 简介
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,比如非常复杂的查找、过滤和映射数据等,在使用过程中允许并行执行这些操作。简言之,Stream API 提供了一种 高效且易于使用的处理数据的方式。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,关注的是数据的存储,而 Stream 是有关计算的,关注的是数据之间的运算。
注意:
- Stream 本身是不会存储数据
- Stream 不会改变源数据,它们会返回一个持有新结果的Stream
- Stream 操作是延迟进行的,它们是在需要结果的时候才会去执行
Stream 的执行流程
- Stream的实例化
- 一系列的中间操作(过滤、映射、…)
- 终止操作
2 Stream的实例化
从一个数据源(集合、数组)中获取一个流
Stream的实例化常见的有以下三种方式
2.1 通过集合来创建
通过Collection中提供的方法来创建
@Test
public void test01(){
List<String> list = new ArrayList<>();
//default Stream<E> stream() : 返回一个顺序流
Stream<String> stream = list.stream();
//default Stream<E> parallelStream() : 返回一个并行流
Stream<String> stringStream = list.parallelStream();
}
2.2 通过数组来创建
通过 Arrays 的静态方法 stream() 可以获取数组流
@Test
public void test02(){
// static <T> Stream<T> stream(T[] array): 返回一个流
Stream<String> stream = Arrays.stream(new String[]{"1", "2", "3", "4", "5", "6"});
}
2.3 通过Stream创建
通过Stream类静态方法 of() 来创建
@Test
public void test03(){
// static<T> Stream<T> of(T... values) : 返回一个流
Stream<String> stringStream = Stream.of("1", "2", "3", "4", "5");
}
2.4 创建无限流
这种方式创建流不是很常见
@Test
public void test04(){
// 迭代
// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
stream.limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream<Double> stream1 = Stream.generate(Math::random);
stream1.limit(10).forEach(System.out::println);
}
3 中间操作
一个中间操作链,对数据源的数据进行处理,如果不执行终止操作,是看不到操作结果的
主要有一下三种类型
3.1 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda,从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的hashcode()和equals()去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
@Test
public void test05(){
List<Student> students = getStudent();
// filter(Predicate p) 接收 Lambda , 从流中排除某些元素
students.stream().filter(e->e.getScore()>80).forEach(System.out::print);
System.out.println();
// limit(long maxSize) 截断流,使其元素不超过给定数量
students.stream().limit(2).forEach(System.out::print);
System.out.println();
// skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
students.stream().skip(2).forEach(System.out::print);
System.out.println();
// distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
students.add(Student.builder().id(5).name("三毛").age(18).score(80.0).build());
students.add(Student.builder().id(5).name("三毛").age(18).score(80.0).build());
students.stream().distinct().forEach(System.out::print);
}
3.2 映射
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
@Test
public void test06(){
// map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::print);
System.out.println();
// flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
list.stream().map(StreamTest::getCharacter).forEach(e->e.forEach(System.out::print));
System.out.println();
list.stream().flatMap(StreamTest::getCharacter).forEach(System.out::print);
}
public static Stream<Character> getCharacter(String str){
List<Character> characters = new ArrayList<>();
for (char c : str.toCharArray())
characters.add(c);
return characters.stream();
}
3.3 排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
@Test
public void test07(){
// sorted() 产生一个新流,其中按自然顺序排序
List<Integer> list = Arrays.asList(12, 355, 3, 23, -6, 4, 5);
list.stream().sorted().forEach(System.out::println);
// sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
getStudent().stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
}
4 终止操作
4.1 匹配和查找
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代 |
@Test
public void test08(){
List<Student> student = getStudent();
// allMatch(Predicate p) 检查是否匹配所有元素
System.out.println(student.stream().allMatch(e -> e.getAge() > 18)); // 检查学生年龄是否都大于18岁
// anyMatch(Predicate p) 检查是否至少匹配一个元素
System.out.println(student.stream().anyMatch(e -> e.getAge() < 18)); // 检查学生年龄是否有小于18岁的
// noneMatch(Predicate p) 检查是否没有匹配所有元素
System.out.println(student.stream().noneMatch(e -> e.getAge() < 9)); // 检查学生年龄是否都不小于9岁
// findFirst() 返回第一个元素
System.out.println(student.stream().findFirst());
// findAny() 返回当前流中的任意元素
System.out.println(student.stream().findAny());
// count() 返回流中元素总数
System.out.println(student.stream().count());
// max(Comparator c) 返回流中最大值
System.out.println(student.stream().max(Comparator.comparingInt(Student::getAge)));
// min(Comparator c) 返回流中最小值
System.out.println(student.stream().min(Comparator.comparingInt(Student::getAge)));
// forEach(Consumer c) 内部迭代
student.stream().forEach(System.out::println);
}
4.2 归约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
@Test
public void test09(){
// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T identity 这是一个附加值
System.out.println(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().reduce(0, Integer::sum)); // 计算1——9的总和
// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 OptionalSystem.out.println(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().reduce(Integer::sum));
}
4.3 收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总的方法 |
@Test
public void test10(){
// collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总的方法
List<Student> student = getStudent();
// 查找分数大于80的学生,结果返回一个List集合
List<Student> collect = student.stream().filter(e -> e.getScore() > 80).collect(Collectors.toList());
collect.forEach(System.out::println);
}