不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2022-05-02 20:06:46  修改时间:2024-03-29 01:33:25  分类:Java基础  编辑

一、什么是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