Java Stream

概述

Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。

2020110911333574

stream是什么?

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

操作分类

(Stream可以由数组或集合创建)

1,中间操作 (每次返回一个新流,可以有多个)

2,终端操作 (每个流只能进行一次终端操作,终端操作后流无法使用。终端操作会产生一个新的集合或值)

特性

1,不存储数据,按照特定规则对数据进行计算,输出结果

2,不改变数据源,通常产生新集合或者值

3,延迟执行特性,只有调用终端操作时,中间操作才执行

​ (函数式编程,我只提供要什么,怎么做是系统的事)

创建

集合创建流

通过java.util.collection.stream()方法用集合创建流

List<String> list=Arrays.asList("1","2","3");
// 创建顺序流
Stream<String> stream=list.stream();
// 创建并行流
Stream<String> parallelStream=list.parallelStream();

stream和parallelStream简单区分

stream是顺序流,由主线程按顺序对流操作

parallelStream是并行流,内部以多线程并行的方式对流操作(前提:流中数据没有顺序要求)

除了直接创建并行流,还可以通过 parallel() 把顺序流转换成并行流

20201106164400889

数组创建流

使用java.util.Arrays.stream(T[] array) 方法用数组创建流

int[] array={1,2,3,4,5};
IntStream stream=Arrays.stream(array);

静态方法创建流

使用Stream的静态方法:of() iterator() generate()

Stream<Integer> stream=Stream.of(1,2,3,4);

Stream<Integer> Stream2=Stream.iterate(0,(x) -> x+3).limit(4);
stream2.forEach(System.out::println);

Stream<Double> Stream3=Stream.generate(Math::random).limit(3);
stream2.forEach(System.out::println);

Stream使用

遍历和匹配(foreach/find/match)

支持类似集合的遍历和匹配元素的,只是中的元素以类型存在

2020110914450139

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
// 遍历输出符合条件的元素
list.stream().filter(x -> x > 6).forEach(System.out::println);
// 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x < 6);
System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);

筛选(filter)

按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作

20201109144706541

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
// 选择薪资大于8000的并将名称转换为list集合
List<String> fiterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
.collect(Collectors.toList());
System.out.print("高于8000的员工姓名:" + fiterList);

聚合(max/min/count)

Java Stream中引入了max,min,count等,便于数据统计

20201109145217354

// 获取String集合中最长元素
List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());

List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
// 自然排序
Optional<Integer> max = list.stream().max(Integer::compareTo);
// 自定义排序
Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);
    }
});
System.out.println("自然排序的最大值:" + max.get());
System.out.println("自定义排序的最大值:" + max2.get());

映射(map/flatMap)

映射,可以将流的元素按照一定比例的映射规则映射到另一个流中,为map和flatMap

map:接收函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新元素

flatMap:接收函数作为参数,将流中的每个值都换成另一个流,将所有流连接成一个流

20201109145252340

// 字母全部改为大写,整数数组每个+3
String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());

System.out.println("每个元素大写:" + strList);
System.out.println("每个元素+3:" + intListNew);
// 不改变原来员工集合的方式
List<Person> personListNew = personList.stream().map(person -> {
    Person personNew = new Person(person.getName(), 0, 0, null, null);
    personNew.setSalary(person.getSalary() + 10000);
    return personNew;
}).collect(Collectors.toList());

// 改变原来员工集合的方式
List<Person> personListNew2 = personList.stream().map(person -> {
    person.setSalary(person.getSalary() + 10000);
    return person;
}).collect(Collectors.toList());
// 将两个数组合并为一个数组
List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
List<String> listNew = list.stream().flatMap(s -> {
    // 将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
}).collect(Collectors.toList());

归约(reduce)

参考文章

Optional reduce(BinaryOperator accumulator);

参数1:累加器(两个参数初始值分别为集合中的第一个和第二个元素,而后第一个为上次结果,第二个为下个集合元素)

T reduce(T identity, BinaryOperator accumulator);

参数1:初始值

参数2:累加器(两个参数初始值分别为初始值和第一个集合元素,而后第一个为上次结果,第二个为下个集合元素)

U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner)

参数1:初始值

参数2:累加器(两个参数初始值分别为初始值和第一个集合元素,而后第一个为上次结果,第二个为下个集合元素)

参数3:参数组合器,用于合并多线程的result值(stream支持并发,每个线程都有独立的result)

归约,也称缩减,是把一个流缩减成一个值,能够实现对集合求和,求乘积和求最值的操作

20201109145706497

List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求和方式1
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);  	
// 求和方式2
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);      
// 求和方式3
Integer sum3 = list.stream().reduce(0, Integer::sum);

// 求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);     

// 求最大值方式1
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);   
// 求最大值写法2
Integer max2 = list.stream().reduce(1, Integer::max);
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

// 求工资之和方式1:
Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
// 求工资之和方式2:
Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
(sum1, sum2) -> sum1 + sum2);
// 求工资之和方式3:
Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

// 求最高工资方式1:
Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
Integer::max);
// 求最高工资方式2:
Integer maxSalary2 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),(max1, max2) -> max1 > max2 ? max1 : max2);

收集(collect)

收集,可以说是内容最繁多,功能最丰富的部分。从字面上去理解,就是把一个流收集起来,最终是可以收集成一个值也可以收集成一个新的集合。

collect主要依赖java.util.stream.Collectors类内置的静态方法

归集(toList、toSet、toMap)

流不存储数据,因而流中的数据完成处理后,要将流中的数据重新归集到新的集合中。

toList、toSet、toMap比较常用,还有toCollection、toConcurrentMap等复杂用法。

List<Integer> list=Arrays.asList(1,2,4,5,2,5,6);
List<Integer> listNew=list.stream().filter(x -> x%2 ==0).collect(Collectors.toList());
Set<Integer> set=list.stream().filter(x -> x % 2 ==0).collect(Collectors.toSet());

List<Person> personList=new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));

Map<?,Person> map=personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName,p -> p));
统计(count、averaging)

collectors 提供了一系列用于数据统计的静态方法:

计数:count

平均值:averagingInt averagingLong averagingDouble

最值:maxBy、minBy

求和:summingInt、summingLong、summingDouble

统计以上所有:summarizingInt、summarizingLong、summarizingDouble

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

求总数
Long count=personList.Stream().collect(Collectors.counting());
求平均值
Double average=personList.Stream().collect(Collectors.averagingDouble(Person::getSalaray))
求最高工资
Optional<Integer> max=personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
求工资之和
Integer sum=personList.stream().collect(Collectors.summingInt(Person::getSalary));
一次性统计所有信息
DoubleSummaryStatistics collect=personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
分组(partitioningBy、groupingBy)

分区:将stream按条件分为两个Map,例如员工按薪资是否高于8000分为两个部分

分组:将集合分为多个Map,比如员工按性别分组,有单级分组和多级分组

20201109145807450

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, "male", "New York"));
personList.add(new Person("Jack", 7000, "male", "Washington"));
personList.add(new Person("Lily", 7800, "female", "Washington"));
personList.add(new Person("Anni", 8200, "female", "New York"));
personList.add(new Person("Owen", 9500, "male", "New York"));
personList.add(new Person("Alisa", 7900, "female", "New York"));

工资以8000分界线,分两组
Map<Boolean,List<Person>> part=personList.stream().collect(
	Collectors.partitioningBy(x -> x.getSalary() > 8000)
);
按照性别分组
Map<String,List<Person>> group=personList.stream().collect(Collectors.groupingBy(Person::getSex));
员工先按性别分组,再按地区分组
Map<String,Map<String,List<Person>>> group2=personList.stream().collect(
	Collectors.groupingBy(
		Person::getSex,Collectors.groupingBy(Person::getArea)
	)
);
接合(joining)

joining 可以将stream中的元素用特定的连接符(没有的话,直接连接)连接成一个字符串

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

String name=personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
结果:Tom,Jack,Lily

List<String> list=Arrays.asList("A","B","C");
String string=list.stream().collect(Collectors.joining("-"));
结果:A-B-C
归约(reducing)

collectors 类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

每个员工减去起征点后的薪资之和
Integer sum=personList.stream().collect(
	Collectors.reducing(0,Person::getSalary,(i,j) - > (i+j-5000))
);
stream的reduce
Optiona<Integer> sum2=personList.stream().map(Person::getSalary).reduce(Integer::sum);

排序(sorted)

sorted,中间操作

排序方式:1,sorted():自然排序,流中元素要实现Comparable接口

​ 2,sorted(Comparator com):Comparator排序器自定义排序

工资由高到低(同工资按年龄由大到小)
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
personList.add(new Person("Lily", 8800, 26, "male", "New York"));
personList.add(new Person("Alisa", 9000, 26, "female", "New York"));

按工资升序排序(默认升序)
List<String> newList=personList.stream().sorted(
	Comparator.comparing(Person::getSalary)
).map(Person::getName).collect(Collectors.toList());
按工资倒序排序
List<String> newList2=personList.stream().sorted(
	Comparator.comparing(Person::getSalary).reversed()
).map(Person::getName).collect(Collectors.toList());
按工资再按年龄升序排序
List<String> newList3=personList.stream().sorted(
	Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)
).map(Person::getName).collect(Collectors.toList());
按工资再按年龄自定义排序(降序)
List<String> newList4=personList.stream().sorted(
	(p1,p2)->{
		if(p1.getSalary()==p2.getSalary()){
			return p2.getAge() - p1.getAge();
		}else{
			return p2.getSalary() - p1.getSalary();
		}
	}
).map(Person::getName).collect(Collectors.toList());

提取、组合

流也可以进行合并,去重,限制,跳过等骚操作

20201109145946301

20201109150001270

20201109150012790

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);

合并两个流,去重
List<String> newList=Stream.concat(stream1,stream2).distinct()
.collect(Collectors.toList());
限制从流中获取前n个数据
List<Integer> collect=Stream.iterate(1,x -> x+2).limit(10)
.collect(Collectors.toList());
跳过前n个数据
List<Integer> collect2=Stream.iterate(1,x->x+2).skip(1).limit(5)
.collect(Collectors.toList());

参考文章

Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

posted @ 2022-04-15 15:42  lifelikeplay  阅读(182)  评论(0编辑  收藏  举报