- 一、什么是stream
- 二、Stream操作分类
- 三、Stream特性
- 四、数据转换为Stream
- 五、Stream转换得到指定类型数据
- 六、Stream对于基本类型的封装
- 七、Stream的串行与并行
- 八、Stream中间操作(有状态)常用API
- 九、Stream中间操作(无状态)常用API
- 十、Stream终端操作(短路操作)常用API
- 十一、Stream终端操作(非短路操作)常用API
一、什么是stream
在 Java8 中增加了一个新的抽象接口 Stream API,使用 Stream 操作集合类似于使用 SQL 语句数据库查找数据类似,提供直观的方法进行操作。
Stream 将要处理的元素集合看作是一种流,流在管道中传输,并且可以在管道传输过程中对流进行处理,比如筛选、排序、聚合等操作
一个Java8 的stream是由三部分组成的。数据源,零个或一个或多个中间操作,一个或零个终止操作。
中间操作是对数据的加工,注意,中间操作是lazy操作,并不会立马启动,需要等待终止操作才会执行。终止操作是stream的启动操作,只有加上终止操作,stream才会真正的开始执行。
二、Stream操作分类
Stream 操作分为 中间操作、终端操作,信息如下:
分类 | 子分类 | 方法 |
---|---|---|
中间操作 | 无状态 | unordered()、filter()、map()、mapToInt()、mapToLong() mapToDouble()、flatMap()、flatMapToInt()、flatMapToLong() flatMapToDouble()、peek() |
有状态 | distinct()、sorted()、limit()、skip() | |
终端操作 | 非短路操作 | forEach()、forEachOrdered()、toArray()、reduce()、collect、max()、min()、count() |
短路操作 | anyMatch()、allMatch()、noneMatch()、findFirst()、findAny() |
中间操作总是会惰性执行:中间操作其实就是进行逻辑处理,会生成一个标记了该操作的新Stream,这个操作可以有一个或多个连续操作,将一个流转换成另一个流,这些操作不会消耗流,其目的是建立一个流水线,直到终端操作发生后,才会做数据的最终执行。
- 无状态:指数据处理时,不受之前“中间操作”的影响
- 有状态:指数据处理时,受之前“中间操作”的影响,有状态的方法往往需要更大的性能开销
终端操作会触发实际结果:一个Stream对象只能有一个终端操作,这个操作一旦发生,就会把所有中间操作积攒的操作以管道的方式执行,这样可以减少迭代次数,生成对应的处理结果,最终stream失效。
- 非短路操作:指必须处理所有元素才能得到最终结果
- 短路操作:指遇到某些符合条件的元素就可以得到最终结果
区分中间操作和结束操作最简单的方法,就是看方法的返回值,返回值为stream的大都是中间操作,否则是结束操作。
三、Stream特性
- 无存储:Stream不会对数据进行存储,而是按照特定的规则对数据进行处理
- 不改变数据源:Stream通常不会改变原有数据源,操作时一般是对原有的数据源创建副本,然后对副本进行处理,生成新的Stream
- 具有懒惰化:Stream 很多操作是有向后延迟的,需要一直等到它弄清楚了最后需要多少数据才会开始。而中间操作永远是惰性化的。
- 可以是无限的:集合有固定大小,Stream则不一定。limit(n)和findFirst()这类的短路操作,可以对无限的Stream快速完成运算处理。
- 支持并行能力:Stream是支持并行能力的,可以轻松执行并行化对数据进行处理
- 可被消耗的:Stream的生命周期中,Stream的元素只被消费一次。与迭代器一样,必须生成新的Stream后,才能重新访问与开始源中相同的元素。
四、数据转换为Stream
一般我们需要获取 Stream
对象后才能对其进行操作,下面列出了一些数据转换为 Stream
的常用方法:
/** 数据转换为Stream */ @Test public void a1() { // 多个数据直接转换为Stream Stream<String> stream1 = Stream.of("a", "b", "c"); System.out.println(stream1); // java.util.stream.ReferencePipeline$Head@33d53216 // 数组转换为 Stream String[] strArrays = {"a", "b"}; Stream<String> stream2 = Arrays.stream(strArrays); System.out.println(stream2); // java.util.stream.ReferencePipeline$Head@69a2b3b6 // list列表转换为 Stream ArrayList<String> strList = new ArrayList<>(); strList.add("a"); strList.add("b"); Stream<String> stream3 = strList.stream(); System.out.println(stream3); // java.util.stream.ReferencePipeline$Head@4f3e7344 // set 集合转换为 Stream Set<String> strSet = new HashSet<>(); strSet.add("a"); strSet.add("b"); Stream<String> stream4 = strSet.stream(); System.out.println(stream4); // java.util.stream.ReferencePipeline$Head@7808f638 // map集合转换为Stream HashMap<String, Integer> map = new HashMap<>(); map.put("a", 100); map.put("b", 200); Stream<Map.Entry<String, Integer>> stream5 = map.entrySet().stream(); System.out.println(stream5); // java.util.stream.ReferencePipeline$Head@62d73ead }
五、Stream转换得到指定类型数据
既然可以把集合或者数组转换成流,那么也就可以把流转换回去,使用collect()方法就能实现,不过一般还需要配合 Collectors 工具类一起使用, Collectors 类中内置了一系列收集器实现,如下:
- toList():将元素收集到一个新的 List 集合中
- toSet():将元素手机到一个新的 Set 集合中
- toCollection():将元素收集到一个新的 ArrayList 集合中
- joining():将元素收集到一个可以用分隔符指定的字符串中
/** Stream 转换得到指定类型数据 */ @Test public void a2() { // Stream 转换位字符串 Stream<String> stream2 = Stream.of("a", "b", "c", "a"); String str = stream2.collect(Collectors.joining()).toString(); System.out.println(str); // abca // Stream 转换为 List列表 List<String> strList = stream2.collect(Collectors.toList()); System.out.println(strList); // [a, b, c, a] // Stream转换为Set集合 Set<String> collect1 = stream2.collect(Collectors.toSet()); System.out.println(collect1); // [a, b, c] }
六、Stream对于基本类型的封装
因为Java的泛型不支持基本类型,所以我们无法用 Stream<int> 这样的类型,如果写了类似代码在编译器中会提示编译错误。为了存储int,只能使用 Stream<Integer> ,但这样会产生频繁的装箱、拆箱操作。为了提高效率,对基本类型 int、long、double 进行了封装,分别为 IntStream、LongStream、DoubleStream,使用方法和 Stream类似,示例代码如下:
/** Stream 对于基本类型的封装 */ @Test public void a4() { // IntStream IntStream.of(new int[]{10,20,30}).forEach(System.out::println); // 10 20 30 IntStream.range(1,5).forEach(System.out::println); // 1 2 3 4 IntStream.rangeClosed(1,5).forEach(System.out::println); // 1 2 3 4 5 // LongStream LongStream.of(new long[]{1L, 2L, 3L}).forEach(System.out::println); // 1 2 3 LongStream.range(1,5).forEach(System.out::println); // 1 2 3 4 LongStream.rangeClosed(1,5).forEach(System.out::println); // 1 2 3 4 5 // DoubleStream DoubleStream.of(1.11, 2.23, 3.14).forEach(System.out::println); // 1.11 2.23 3.14 }
七、Stream的串行与并行
7.1 Stream的并行介绍
在Stream中,最明显的特点就是存在并行操作,不过如果使用默认方式执行中间与终端操作,那么整个执行过程其实是串行操作。如果想让Stream并行处理数据,那么需要Stream中调用parallel()
或者集合中调用parallelStream()
方法来开启并行执行。
@Test public void a5() { List<String> list = Arrays.asList("One", "Two", "three", "Four", "Five"); Stream<String> stream = list.stream().parallel(); System.out.println(stream); // 第二种写法 Stream<String> stringStream = list.parallelStream(); System.out.println(stringStream); }
其中Stream底层使用的是 ForkJoinTask 实现 Stream的并行处理,充分利用CPU 的多核能力,Stream的API将底层复杂实现完全屏蔽了,开发者仅需调用一个方法即可实现并行计算。
7.2 Stream的串行和并行运行时间
@Test public void a5() { //并行计算 long startTime = System.currentTimeMillis(); System.out.println(startTime); long sumResult1 = LongStream.rangeClosed(1, 10000000000L).parallel().sum(); System.out.println(System.currentTimeMillis() - startTime); // 1384 // 串行计算 startTime = System.currentTimeMillis(); long sumResult2 = LongStream.rangeClosed(1, 10000000000L).sum(); System.out.println(System.currentTimeMillis() - startTime); // 4134 }
可以看到使用并行执行所花费的时间远低于串行所花费的时间,不过在使用并行执行时一定要先考虑好使用情况,考虑执行数据是否需要顺序执行,是否涉及线程安全,是否涉及使用网络等。
八、Stream中间操作(有状态)常用API
stream跟函数接口关系非常紧密,没有函数接口stream就无法工作。回顾一下:函数接口是指内部只有一个抽象方法的接口。通常函数接口出现的地方都可以使用Lambda表达式,所以不必记忆函数接口的名字。
8.1 distinct 去重
保证输出的流中包含唯一的元素,通常用于数据去重。
- 接口定义: Stream<T> distinct();
- 方法描述:在distinct 接口定义中不接收任何参数,该方法的作用是根据 hashCode() 和 equals() 方法来获取不同的元素,因此我们的元素必须实现这两个方法。如果distinct() 正在处理有序流,那么对于重复元素将保留元素时的顺序。而在处理无序流的情况下,则不一定保证元素的顺序。在有序流的并行执行情况下,保持distinct() 的顺序性是需要高昂的缓冲开销。如果我们在处理元素时,不需要保证元素的顺序性,那么我们可以使用 unordered() 方法来实现无序流。
/** distinct() 方法 */ @Test public void a6() { List<Integer> strList = new ArrayList<>(); strList.add(4);strList.add(2);strList.add(2);strList.add(1);strList.add(3); strList.stream().distinct().unordered().forEach(System.out::println); // 4 2 1 3 } // 项目中的案例 优势就是不需要再写去重的sql语句 // 有状态:指数据处理时,受之前“中间操作”的影响,有状态的方法往往需要更大的性能开销 List<String> orgIdList = programDimensionList.stream().map(BackcalProgramDimension::getOrganizationId).distinct().collect(Collectors.toList());
8.2 sorted 排序
对数据进行排序,不过对于有序流,排序是稳定的,而对于非有序流,不保证排序稳定。
- 接口定义:Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
- 方法描述:
- 使用 Stream<T> sorted(); 方法时,它会将 Stream中的元素按照 自然排序 方式对元素进行排序。等到将全部元素处理完成后,将元素组成新的Stream返回。
- 使用 Stream<T> sorted(Comparator<? super T> comparator); 方法时,它接收的是一个 Comparator 类型参数,在Lambda表达式中 Comparator<T> 一般是用于比较两个参数,设计一个算法逻辑,执行完后返回一个整数,可以是负数、零、正整数,它的不同的值表示两个值比较的不同,一般排序会按这个比较结果进行排序。等到将全部元素处理完成后,将元素组成新的 Stream 返回。
/** sorted 方法 */ @Test public void a7() { List<Integer> strList = new ArrayList<>(); strList.add(4);strList.add(2);strList.add(1); strList.stream().sorted((x, y) -> x-y).forEach(System.out::println); // 1 2 4 用了比较器进行比较 从小到大 } // 项目中用到的排序,不是自然排序,通过比较器进行比较,如果从高到低,比较器后面加上reversed(),最后获取的是 getVariableCode这个字段的值 List<String> variableCodeList = variableList.stream() .sorted(Comparator.comparing(CalculationVariable::getVariableCode).reversed()) .map(f -> f.getVariableCode()) .distinct() .collect(Collectors.toList());
8.3 skip 跳过
根据指定数值,从指定位置跳过流中某些元素
- 接口定义: Sream<T> skip(long n);
- 方法描述:在skip接口定义中是接收 long 类型参数,指定要跳过前n个元素,将第n个后的元素组成新的流返回
/** skip 方法 */ @Test public void a8() { List<Integer> strList = new ArrayList<>(); strList.add(1);strList.add(2);strList.add(3); strList.stream().skip(2).forEach(System.out::println); // 3 }
8.4 limit 限制展示几个
根据指定数值,限制只能访问流中最大访问的个数。这是一个有状态的、短路方法。
- 接口定义:Stream<T> limit(long maxSize);
- 方法描述:在limit 接口定义中是接收 long 类型参数,指定限制 maxSize 个元素,即将 maxSize 和它之前的元素组成新的流返回
/** limit 方法 */ @Test public void a9() { List<Integer> strList = new ArrayList<>(); strList.add(1);strList.add(2);strList.add(3);strList.add(4);strList.add(5); strList.stream().limit(2).forEach(System.out::println); // 1 2 }
九、Stream中间操作(无状态)常用API
9.1 map
对流中的元素进行处理,然后返回,返回值的类型可以和原来的元素的类型不同
- 接口定义:Stream<R> map(Function<? super T, ? extends R> mapper);
- 方法描述:在map接口定义中是接收 Function 类型参数,了解 Lambda 表达式就可以知道 Function<T,R> 是接收一个 T 返回处理后的值 R。所以,这里map方法就是对流中的元素进行处理,然后返回一个新的元素。等到将全部元素处理完成后将元素组成新的流返回。
/** map */ @Test public void a10() { List<String> strList = new ArrayList<>(); strList.add("zhangsan");strList.add("lisi");strList.add("wangwu");strList.add("zhaoliu"); strList.add("sunqi"); strList.stream().map(x -> "测试:" + x).collect(Collectors.toList()).forEach(System.out::println); } 测试:zhangsan 测试:lisi 测试:wangwu 测试:zhaoliu 测试:sunqi // 项目中的案例 // 判断考核期是否存在考核期表内 boolean isExitPeriod = periodList.stream().map(Period::getPeriod).collect(Collectors.toList()).contains(period);
9.2 peek
对流中的每个元素进行操作处理,返回的流和原来的流保存一样的元素
- 接口定义:Stream<T> peek(Consumer<? super T> action);
- 方法描述:在peek 接口定义中是接收 Consumer 类型参数,了解 Lambda 表达式就可以知道 Consumer<T> 是接收一个T返回处理后不返回值。所以,这里peek方法就是对流中的元素进行处理而不返回值。等到将全部元素处理完成后将元素组成新的流返回。
/** peek */ @Test public void a11() { List<String> strList = new ArrayList<>(); strList.add("zhangsan"); strList.add("lisi"); strList.add("wangwu"); strList.stream().peek(x -> System.out.println("forEach 1:" + x)) .peek(x -> System.out.println("forEach 2:" + x)) .forEach(System.out::println); } forEach 1:zhangsan forEach 2:zhangsan zhangsan forEach 1:lisi forEach 2:lisi lisi forEach 1:wangwu forEach 2:wangwu wangwu
自己对peek的理解,就是对stream流中的元素输出单个的值,方便调式,方便看到每个变量的值。
9.3 filter
主要用于数据过滤,过滤不符合 predicate 条件的元素,保留过滤后的元素
- 接口定义: Stream<T> filter(Predicate<? super T> predicate);
- 方法描述: 在 filter 接口定义中是接收 Predicate 类型参数,了解Lambda表达式就可以知道 Predicate<T> 是接收一个T进行验证,返回布尔值。所以,这里filter方法就是设置验证条件,对流中的元素进行验证,返回符合条件的全部元素组成新的流。
/** filter */ @Test public void a12() { List<String> strList = new ArrayList<>(); strList.add("zhangsan");strList.add("lisi");strList.add("wangwu");strList.add("zhaoliu");strList.add("sunqi"); strList.stream().filter(x -> x.length()>5).collect(Collectors.toList()).forEach(System.out::println); } zhangsan wangwu zhaoliu
9.4 mapToInt
主要用于对流中元素进行一对一转换为int整数,然后可以进行一些求和、平均值、最大最小值等处理。
- 接口定义:IntStream mapToInt(ToIntFunction<? super T> mapper);
- 方法描述:在mapToInt 接口定义中可知,它接收 ToIntFunction 类型参数,在Lambda中ToIntFunction<T> 函数的作用为接收一个输入参数,返回一个int 类型结果,根据这点很容易了解到mapToInt方法就是将Stream中原有的元素类型转换为int类型到新的Stream中。
/** mapToInt */ @Test public void a13() { Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int max = integerStream.mapToInt(x -> x).summaryStatistics().getMax(); System.out.println(max); // 10 IntSummaryStatistics intSummaryStatistics = integerStream.mapToInt(x -> x).summaryStatistics(); System.out.println(intSummaryStatistics); // IntSummaryStatistics{count=10, sum=55, min=1, average=5.500000, max=10} } // 项目中使用到的示例: workDay = BigDecimal.valueOf(workDayCountListGroupById.get(employeeId).stream().mapToInt(PorationModel::getCount).sum());
9.5 mapToDouble
主要用于对流中元素进行一对一转换为double双精度浮点数,然后可以进行一些求和、平均值、最大最小值等处理。
- 接口定义:DoubleStream mapToDouble(ToIntFunction<? super T> mapper);
- 方法描述:在 mapToDouble 接口定义中可知,它接收 ToDoubleFunction 类型参数,在Lambda中ToDoubleFunction <T> 函数的作用为接收一个输入参数,返回一个Double类型结果,根据这点很容易了解到mapToDouble 方法就是将Stream中原有的元素类型转换为Double类型到新的Stream中。
/** mapToDouble */ @Test public void a14() { Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); double max = integerStream.mapToDouble(x -> x).summaryStatistics().getMax(); System.out.println(max); // 10.0 DoubleSummaryStatistics doubleSummaryStatistics = integerStream.mapToDouble(x -> x).summaryStatistics(); System.out.println(doubleSummaryStatistics); // DoubleSummaryStatistics{count=10, sum=55.000000, min=1.000000, average=5.500000, max=10.000000} }
十、Stream终端操作(短路操作)常用API
10.1 anyMatch
判断数据列表中是否存在任意一个元素符合设置的predicate条件,如果是就返回true,否则返回false。
- 接口定义:boolean anyMatch(Predicate<? super T> predicate);
- 方法描述:在anyMatch 接口定义中是接收 Predicate 类型参数,在Lamdba表达式中 Predicate<T> 是接收一个T类型参数,然后经过逻辑验证返回布尔值结果。这里anyMatch表示,判断的条件里,任意一个元素符合条件,就返回true值。
- 使用场景:两个集合的交集
@Test public void a17() { List<User> list = new ArrayList<>(); list.add(new User("张三", 12, "南京")); list.add(new User("李四", 13, "北京")); list.add(new User("王五", 14, "苏州")); list.add(new User("王五", 17, "苏州")); List<User> userList = new ArrayList<>(); userList.add(new User("李四", 13, "北京")); userList.add(new User("王五", 20, "广州")); // 获取两个集合中有相同名字或者年龄相同的,只要满足其中一个条件即可,只会返回list集合里面的元素,有先后顺序返回 List<User> users1 = list.stream() .filter(a -> userList.stream().anyMatch(b -> a.getName().equals(b.getName()) || a.getAge() == b.getAge())) .collect(Collectors.toList()); // 获取两个集合中相同名字并且年龄相同的,必须同时满足两个条件 List<User> users2 = list.stream() .filter(a -> userList.stream().anyMatch(b -> a.getName().equals(b.getName()) && a.getAge() == b.getAge())) .collect(Collectors.toList()); users1.forEach(item -> { System.out.println(item.getName() + item.getAge() + item.getEmailAddress()); }); /** 第一种结果展示: * 李四13北京 * 王五14苏州 * 王五17苏州 */ users2.forEach(item -> { System.out.println(item.getName() + item.getAge() + item.getEmailAddress()); }); /** 第二种结果展示: * 李四13北京 */ } @Test public void a15() { Stream<String> stream = Stream.of("ac", "bcddddd", "bd"); // 判断stream中其中任何一个元素中只要有包含b字符串或者l字符串就返回true boolean isMatch = stream.anyMatch(str -> str.contains("b") || str.contains("l")); System.out.println(isMatch); // true }
10.2 allMatch
判断数据列表中全部元素都符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。
- 接口定义:boolean allMatch(Predicate<? super T> predicate);
- 方法描述:在allMatch 接口定义中是接收 Predicate 类型参数,在Lamdba表达式中 Predicate<T> 是接收一个T类型参数,然后经过逻辑验证返回布尔值结果。这里allMatch表示,判断的条件里,全部元素符合条件,就返回true值。
- 适用场景:
- 基本类型的集合,但不适合于对象集合(我自己的理解)
- allMatch里面不适合写 && ,只适合写 ||,如果写&&,编译器会自动报黄色波浪线
@Test public void a18() { List<String> typeList1 = Arrays.asList("1", "2"); List<String> typeList2 = Arrays.asList("1", "2", "3", "4"); // 集合列表中全部元素必须在allMatch里面的那些字符串,只要全部元素中有任意一个不同的元素在AllMatch中就返回false boolean isMatch1 = typeList1.stream().allMatch(a -> a.equals("1") || a.equals("2") || a.equals("3")); boolean isMatch2 = typeList2.stream().allMatch(a -> a.equals("1") || a.equals("2") || a.equals("3")); System.out.println(isMatch1); // true System.out.println(isMatch2); // false } @Test public void a16() { Stream<String> stream = Stream.of("abc", "abc", "bcd"); // 判断stream中全部所有元素必须全部包含b字符串和c字符串就返回true,如果有一个元素不包含这两个字符串就返回false boolean isMatch = stream.allMatch(str -> str.contains("b") && str.contains("c")); System.out.println(isMatch); // true }
10.3 noneMatch
判断数据列表中全部元素都不符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。
- 接口定义:boolean noneMatch(Predicate<? super T> predicate);
- 方法描述:在noneMatch接口定义中是接收 Predicate 类型参数,在Lamdba表达式中 Predicate<T> 是接收一个T类型参数,然后经过逻辑验证返回布尔值结果。这里noneMatch表示与allMatch相反,判断的条件里的元素,所有的元素都不符合,就返回true值。
- 适用场景:两个集合的差集 (本人只想到这么做,如果有更简洁的可以告诉我怎么写,感谢0.0)
@Test public void a17() { List<User> list = new ArrayList<>(); list.add(new User("张三", 12, "南京")); list.add(new User("李四", 13, "北京")); list.add(new User("王五", 14, "苏州")); list.add(new User("王五", 17, "苏州")); List<User> userList = new ArrayList<>(); userList.add(new User("李四", 13, "北京")); userList.add(new User("王五", 20, "广州")); // 获取list集合和userList集合过滤掉两者集合中名字和年龄相同的数据后,只返回list集合的数据 List<User> users3 = list.stream() .filter(a -> userList.stream().noneMatch(b -> a.getName().equals(b.getName()) && a.getAge() == b.getAge())) .collect(Collectors.toList()); // 获取userlist集合和list集合过滤掉两者集合中名字和年龄相同的数据后,只返回userList集合的数据 List<User> users4 = userList.stream() .filter(a -> list.stream().noneMatch(b -> a.getName().equals(b.getName()) && a.getAge()==b.getAge())) .collect(Collectors.toList()); // 获取list和userList集合之间的差集,将上面两者集合合并,就是两个集合的差集 List<User> arrayList = new ArrayList<>(); arrayList.addAll(users3); arrayList.addAll(users4); arrayList.forEach(item -> { System.out.println(item.getName() + item.getAge() + item.getEmailAddress()); }); /** 两者集合之间的差集 * 张三12南京 * 王五14苏州 * 王五17苏州 * 王五20广州 */ System.out.println("-------------------"); users3.forEach(item -> { System.out.println(item.getName() + item.getAge() + item.getEmailAddress()); }); /** 只返回list集合中过滤掉之后的元素集合 * 张三12南京 * 王五14苏州 * 王五17苏州 */ System.out.println("-------------------"); users4.forEach(item -> { System.out.println(item.getName() + item.getAge() + item.getEmailAddress()); }); /** 只返回userList集合中过滤掉之后的元素集合 * 王五20广州 */ } @Test public void a19() { List<String> typeList1 = Arrays.asList("1", "2"); List<String> typeList2 = Arrays.asList("1", "2", "3", "4"); // 集合列表中全部元素只要都不在noneMatch里面的判断中,就返回true,集合列表中任何元素中只要有一个在noneMatch的判断中就返回false boolean isMatch1 = typeList1.stream().noneMatch(a -> a.equals("3") || a.equals("4")); boolean isMatch2 = typeList2.stream().noneMatch(a -> a.equals("1") || a.equals("2") || a.equals("3")); System.out.println(isMatch1); // true System.out.println(isMatch2); // false } /** noneMatch */ @Test public void a20() { Stream<String> stream = Stream.of("dddd", "ee", "qqq", "bcfff"); // 判断 stream 中所有的元素都不是以 a 开头,就返回true,如果所有的元素中只要其中一个元素是以a开头的,就返回false boolean isMatch = stream.noneMatch(str->str.startsWith("a")); System.out.println(isMatch); // true }
10.4 findFirst
返回第一个元素,如果流为空,返回空的Optional对象。
- 接口定义:Optional<T> findFirst();
- 方法描述:在 findFirst 方法的作用是返回集合中的第一个对象
/** * findFirst */ @Test public void a21() { List<User> list = new ArrayList<>(); list.add(new User("张三", 12, "南京")); list.add(new User("李四", 13, "北京")); list.add(new User("王五", 14, "苏州")); list.add(new User("王五", 17, "苏州")); Optional<User> user = list.stream().filter(a -> a.getName().equals("王五")).findFirst(); if (user.isPresent()) { System.out.println(user.get().getAge()); // 14 } else { System.out.println("查无此人"); } } // 项目中用到过的示例 Optional<MailDataSourceRelation> relation = relationList.stream().filter(f -> f.getColumnKey().equals(item.getColumnKey())).findFirst(); if (relation.isPresent()) { item.setIsUsed("Y"); }
10.5 findAny
返回任意一个元素,如果流为空,返回空的Optional对象。
- 接口定义:Optional<T> findAny();
- 方法描述:在 findAny 方法的作用是返回集合中的任何一个对象。
/** * findAny */ @Test public void a22() { List<User> list = new ArrayList<>(); list.add(new User("张三", 12, "南京")); list.add(new User("李四", 13, "北京")); list.add(new User("王五", 14, "苏州")); list.add(new User("王五", 17, "苏州")); Optional<User> user = list.stream().filter(a -> a.getName().equals("王五")).findAny(); if (user.isPresent()) { System.out.println(user.get().getAge()); // 14 } } // 项目中用到过的示例 经常跟isPresent配合使用 if(list.stream().filter(x -> !x.getId().equalsIgnoreCase(item.getId()) && x.getIndicatorCode().equalsIgnoreCase(item.getIndicatorCode())).findAny().isPresent()){ throw ExpResult.fail("权重项代码已经存在"); }
十一、Stream终端操作(非短路操作)常用API
11.1 max
返回流中所有元素的最大值
- 接口定义:Optional<T> max(Comparator<? super T> comparator);
- 方法描述:在 max接口定义中接收Comparator类型参数,Lambda常用函数的Consumer<T> 一般是用于比较两个参数,设置一个算法逻辑,执行完后返回一个整数,可以是负数、零、正整数,根据返回的值结果进行排序筛选出最大值。
/** * max 使用 */ @Test public void a24() { Stream<Integer> stream = Stream.of(1, 3, 5, 8); Optional<Integer> max = stream.max((x, y) -> x - y); if (max.isPresent()) { System.out.println(max.get()); // 8 } }
11.2 min
返回流中所有元素的最小值
- 接口定义:Optional<T> min(Comparator<? super T> comparator);
- 方法描述:在 min接口定义中接收Comparator类型参数,Lambda常用函数的Consumer<T> 一般是用于比较两个参数,设置一个算法逻辑,执行完后返回一个整数,可以是负数、零、正整数,根据返回的值结果进行排序筛选出最小值。
/** * min 使用 */ @Test public void a25() { Stream<Integer> stream = Stream.of(1, 3, 5, 8); Optional<Integer> min = stream.min(Integer::compareTo); if (min.isPresent()) { System.out.println(min.get()); // 1 } } //项目中使用到的示例 Optional<MeasurementTierSetting> hopsOptional = hopsList.stream().min(Comparator.comparing(MeasurementTierSetting::getRowSeq));
11.3 count
返回流中所有元素的数目
- 接口定义:long count();
- 方法描述:该方法用于统计stream中元素个数,返回long类型结果。
/** * count 使用 */ @Test public void a26() { Stream<Integer> stream = Stream.of(1, 3, 5, 8); long count = stream.count(); System.out.println(count); // 4 } //项目中使用到的示例 long count = ruleAndCommissionItemRelationHistoryList.stream() .filter(e -> e.getCommissionItemId().equals(commissionItemId)).count(); if (count > 0) { commissionItemIds.add(commissionItemId); }
11.4 reduce
主要用于对数据进行合并处理 (对字段的值进行相加或者多字段的值进行相加)
11.5 forEach
主要用于对数据遍历整个流中元素,执行指定逻辑,在并发执行时不保证顺序。
- 接口定义:void forEach(Consumer<? super T> action);
- 方法描述:对 Stream 中的每个元素都执行一段逻辑代码,例如,循环打印输出元素的值,但是需要注意的是,在并发执行时无法保证执行顺序。
/** * forEach使用 */ @Test public void a30() { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); stream.forEach(System.out::println); }
11.6 forEachOrdered
主要用于对数据遍历整个流中元素,执行指定逻辑,在并发执行时保证顺序
- 接口定义:void forEachOrdered(Consumer<? super T> action);
- 方法描述:对 Stream 中的每个元素都执行一段逻辑代码,例如,循环打印输出元素的值,但是需要注意的是,在并发执行时无法保证执行顺序。
/** * forEachOrdered */ @Test public void a31() { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); stream.parallel().forEachOrdered(System.out::println); }
11.7 toArray
将Stream流中的数据存储到数组中
- 接口定义:Object[] toArray(); <A>A[] toArray(IntFunction<A[]> generator);
- 方法描述:将 Stream 中的元素,存储到Object 数组
/** * toArray */ @Test public void a32() { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Object[] objectArray = stream.toArray(); Arrays.stream(objectArray).forEach(System.out::println); }
11.8 collect
常用的聚合方法,能将数据进行聚合操作。不仅如此,它还能与Controller配合使用,对聚合后的数据进行处理。
摘自:https://blog.csdn.net/yuxiangdeming/article/details/122720898