Java Stream
Java Stream
1、什么是Stream
Stream 是 Java8 中处理集合的关键抽象概念。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
Stream 会将要处理的元素集合看作一种流,在流的过程中,借助 Stream API 对流中的元素进行操作,比如:筛选、排序、聚合等
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
2、Stream 的特性:
- stream 不存储数据,二十按照特定的规则对数据进行计算,一般会输出结果
- stream 不会改变数据源,通常情况下会产生一个新的集合或一个值
- stream 具有延迟执行特性,只有调用终端操作时,中间操作才会执行
3、Stream操作分类
可以由数组或集合创建,对流的操作分为两种:
- 中间操作,每次返回一个新的流,可以有多个。(筛选 filter 、映射 map 、排序 sorted 、去重组合 skip–limit)
- 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。(遍历 foreach 、匹配 find-match 、规约 reduce 、聚合 max-min-count 、收集 collect)
Stream操作的三个步骤
1、创建 Stream:一个数据源(如:集合、数组),获取一个流
2、中间操作:一个中间操作链,对数据源的数据进行处理
3、终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果
4、Stream的使用
创建Stream
用集合创建流
List<String> list = new ArrayList<>();
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
顺序流和并行流区分
stream 是顺序流,由主线程按顺序对流执行操作
parallelStream 是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求
如果流中的数据量足够大,并行流可以加快处理速度。
除了直接创建并行流,也可以通过 parallel() 把顺序流转换成并行流:
Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst(); List<Integer> list = Arrays.asList(1,2,3,65,8); System.out.println(findFirst.get());
数组创建流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
Stream中的静态方法创建流
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);//1 2 3 4 5 6
//创建无限流 ①迭代
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(5);
stream2.forEach(System.out::println); // 0 2 4 6 8
Stream<Double> stream3 = Stream.generate(Math::random).//生成无限流:随机数
limit(2);//取前5个值
stream3.forEach(System.out::println);
Stream中间操作
多个中间操作可以连接起来形成一个流水线,除非在流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,也称为“惰性求值”。
常用的中间操作大体上可以分为三类:①筛选与切片;②映射;③排序
筛选与切片
filter(Lambda)——接收Lambda,从流中排除某些元素
limit(n):获取n个元素,截断流,使其元素不超过给定数量,该操作是一个短路操作
skip(n):跳过n元素,配合limit(n)可实现分页,跳过流中的前n个元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct:筛选,通过流所生成元素的hashCode和equals去除重复元素,因此需要流中的元素对象重写hashCode和equals方法
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 10, 12, 14, 14);
Stream<Integer> newStream = stream.filter(s -> s > 5) // 6 7 10 12 14 14
.distinct() //6 7 10 12 14
.skip(2) //10 12 14
.limit(2); //10 12
newStream.forEach(System.out::println);
映射
- map(Function f)——接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素,再将新元素放在一个新流中
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream()
.map(str -> str.toUpperCase())
.forEach(System.out::println);
map会遍历流中的每一个元素,并将元素作为实参传入map中的函数中进行处理,并将返回的值放在一个新流中,最后终止操作时操作的是这个新流。
-
mapToDouble(ToDoubleFunction f) ——接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream
-
mapToInt(ToIntFunction f)—— 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream
-
mapToLong(ToLongFunction f) ——接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream
-
flatMap(Function f) ——接收一个函数作为参数,该函数的返回值必须是一个流(Stream)
将原流中的每个值通过函数都转换成另一个流,然后把转换后的所有流连接成一个流,并实现流的扁平化,即扁平化后的流中的元素不再是一个个的流,而是一个个流中的具体的元素,与map的不同之处在于map是将函数的返回值整个作为新的元素放在一个新的流中,不会扁平化处理,类似于集合中的add(Object obj)和addAll(Collection c)
public void test2() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream()
.flatMap(TestStreamAPI::filterCharacter)//需要一个返回Stream的函数
.forEach(System.out::println);
}
/**
* 该方法必须返回一个Stream
* @param str
* @return
*/
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
排序
- sorted():自然排序,元素对象需实现Comparable接口
List<String> list = Arrays.asList("ccc", "aaa", "ddd", "bbb");
list.stream()
.sorted()//自然排序
.forEach(System.out::println);//aaa bbb ccc ddd
因为String实现了Comparable接口,因此可以使用自然排序
- sorted(Comparator com):定制排序,需要传入Comparator的实现
Employee e1 = new Employee("aa", 10);
Employee e2 = new Employee("bb", 20);
Employee e3 = new Employee("aa", 30);
Employee e4 = new Employee("dd", 40);
List<Employee> employees = Arrays.asList(s1, s2, s3, s4);
employees.stream().sorted((e1, e2) -> {
if (e1.getAge() == e2.getAge())
return e1.getName().compareTo(e2.getName());
else
return e1.getAge().compareTo(e2.getAge());
}).forEach(System.out::println);
按年龄排序,年龄相同按姓名排序
Stream终止操作
终止操作将不再返回流,而是对中间操作产生的流进行具体操作,可能有返回值,也可能没有。如果需要返回值但返回值有可能为空时,终止操作一般会返回一个Optional容器对象,把返回值封装在该容器对象中,可以通过调用Optional.get()获取具体的返回值,以避免出现空指针异常等。
Optional 类是一个可以为 null 的容器对象。
如果值存在则 isPresent() 方法会返回 true,调用 get() 方法会返回该对象。
查找与匹配
- allMatch(Predicate p)——检查是否匹配所有元素,流中的所有元素都满足条件
//注意返回值是boolean,而不再是一个Stream
//0表示在职的员工
boolean allMatch = employees.stream().allMatch((e) -> e.getStatus().equals(0));
- anyMatch(Predicate p)——检查是否至少匹配一个元素,只要流中有一个元素满足条件就返回true
boolean anyMatch = employees.stream().anyMatch((e) -> e.getStatus().equals(0));
- noneMatch(Predicate p)——检查是否没有匹配任何元素,当至少有一个元素匹配时就返回false,当所有元素都不匹配时才返回true,通过该方法得出有匹配的元素的结论
boolean noneMatch = employees.stream().noneMatch((e) -> e.getStatus().equals(0));
- findFirst()——返回第一个元素,调用该方法后会返回一个Optional容器对象
Optional<Employee> findFirst = employees.stream().findFirst();
Employee employee = findFirst.get();
System.out.println(employee);
- findAny()——返回当前流中的任意元素
Optional<Employee> findAny = employees.parallelStream()//并行流
.filter(e -> e.getStatus().equals(1))//过滤
.findAny();
System.out.println(findAny.get());
- count()——返回流中元素总数
long count = employees.stream().count();
- max(Comparator c)——返回流中最大值
Optional<Employee> max = employees.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
- min(Comparator c)——返回流中最小值
Optional<Double> min = employees.stream()
.map(Employee::getSalary)//将流转化为工资数据的流
.min(Double::compareTo);//获取工资的最小值
System.out.println(min.get());
- forEach(Consumer c)——内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
其他方法跨域参考博客
https://blog.csdn.net/rubulai/article/details/88967590