Stream流整理-创新互联
JDK 1.8新增的Stream,配合Lambda,给操作集合(Collection)提供了极大的便利。
网站建设公司,为您提供网站建设,网站制作,网页设计及定制网站建设服务,专注于成都定制网页设计,高端网页制作,对茶艺设计等多个行业拥有丰富的网站建设经验的网站建设公司。专业网站设计,网站优化推广哪家好,专业seo优化排名优化,H5建站,响应式网站。那么什么是Stream?Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream可以由数组或集合创建,对流的操作分为两种:
中间操作,每次返回一个新的流,可以有多个。
终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
另外,Stream有几个特性:
stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
Stream有三种创建方式
通过java.util.Collection.stream()方法用集合创建流
使用java.util.Arrays.stream(T[] array)方法用数组创建流
使用Stream的静态方法:of()、iterate()、generate()
Listlist = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Streamstream = list.stream();
// 创建一个并行流
StreamparallelStream = list.parallelStream();
串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
并行流:适合没有线程安全问题、较单纯的数据处理任务。
int[] array = {1,3,5,6,8};
IntStreamstream= Arrays.stream(array);
2.1.3 使用静态方法Streamstream = Stream.of(1, 2, 3, 4, 5, 6);
Streamstream2 = Stream.iterate(0, (x) ->x + 3).limit(4);
stream2.forEach(System.out::println);
Streamstream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
输出结果:
0 3 6 9
0.6796156909271994
0.1914314208854283
0.8116932592396652
stream和parallelStream的简单区分:stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如:筛选集合中的奇数,两者的处理不同之处:如果流中的数据量足够大,并行流可以加快处速度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:
OptionalfindFirst = list.stream().parallel().filter(x ->x >6).findFirst();
关于更多的Parallel Stream,请参考:Java Parallel Stream和Java Stream之Parallel Streams编程指南
2.2 中间操作 2.2.1 筛选与切片filter(Predicate):筛选流中某些元素
limit(long val):截断流,取流中前val个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct():通过流所生成元素的equals和hashCode去重
map(Function f):接收流中元素,并且将其映射成为新元素,例如:从student对象中取name属性
flatMap(Function f):将所有流中的元素并到一起连接成一个流
peek(Consumer c):获取流中元素,操作流中元素,与foreach不同的是不会截断流,可继续操作。
使用场景:当遍历完数组后还有后续操作时或list数组转stream时,不适合在用Iterable的foreach循环,这个时候peek就派上用场了。2.2.4 排序
sorted()/sorted(Comparator):产生一个新流,按照自然顺序/比较器规则排序
ArrayListnumber = new ArrayList<>();
number.add(12);
number.add(121);
number.add(23);
number.add(45);
number.add(67);
number.add(67);
number.add(77);
number.add(98);
number.add(99);
number.add(67);
Streamdistinct = number.stream().filter(x ->x >50)//121,67,67,77,98,99,67
.skip(2)//67,77,98,99,67
.sorted()//67,67,77,98,99
.distinct()//67,77,98,99
.limit(3);//67,77,98
distinct.forEach(System.out::println);
2.3 终止操作
2.3.1 匹配/聚合操作匹配
allMatch(Predicate):当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch(Predicate):当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch(Predicate):只要流中有一个元素满足该断言则返回true,否则返回false
寻找元素
findFirst():返回流中第一个元素
findAny():返回流中的任意元素
计数和极值
count():返回流中元素的总个数
max():返回流中元素大值
min():返回流中元素最小值
Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
U reduce(U identity, BiFunctionaccumulator, BinaryOperator combiner):在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity, accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行归约。
collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。
Collector
Supplier supplier():创建一个结果容器A
BiConsumer accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T
BinaryOperator combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
Function finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
Set characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
CONCURRENT:表示此收集器支持并发。(官方文档还有其他描述,暂时没去探索,故不作过多翻译)
UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。
collect主要依赖java.util.stream.Collectors类内置的静态方法。Collectors具体方法如下:
归集
toList():将元素收集到一个新的List。
toMap():将元素收集到Map中,Map其键和值是将提供的映射函数应用于元素的结果。
toSet():将元素收集到一个新的Set。
toCollection():将元素Collection按遇到顺序收集到一个new中。
toConcurrentMap():将元素收集到ConcurrentMap的并发对象,其键和值是将提供的映射函数应用于元素的结果。
toUnmodifiableList():将元素收集到一个不可修改的List集合中。任何修改List集合的操作都将导致UnsupportedOperationException。
toUnmodifiableSet():将元素收集到一个不可修改的Set集合中。任何修改Set集合的操作都将导致UnsupportedOperationException。
统计
counting():返回计算元素数量,如果没有元素,则结果为0。
averagingDouble():应用于元素的double值函数的算术平均值。如果没有元素,则结果为0。
averagingInt():应用于元素的int值函数的算术平均值。
averagingLong():应用于元素的long值函数的算术平均值。
summarizingDouble():将生成double映射函数应用于每个元素,并返回结果值的汇总统计信息。
summarizingInt():将生成int映射函数应用于每个元素,并返回结果值的汇总统计信息。
summarizingLong():将生成long映射函数应用于每个元素,并返回结果值的汇总统计信息。
summingDouble():应用于元素的double值函数的总和。
summingInt():应用于元素的int值函数的总和。
summingLong():应用于元素的long值函数的总和。
maxBy():根据给定的比较器产生大元素。
minBy():根据给定的比较器产生最小元素。
分组
groupingBy():根据分类函数对元素进行分组,并返回结果Map。
groupingByConcurrent():并发执行,根据分类函数对元素进行分组。
partitioningBy():对元素进行分区Predicate,并将它们组织成Map
>。
接合
joining():按遇到顺序将元素连接成String。
归约
reducing():在指定的BinaryOperator下执行其元素的缩减。
映射
mapping():通过在累加之前对每个元素应用映射函数。
结果集处理
collectingAndThen():调整Collector执行其它的结束转换。
在使用Stream之前,先理解一个概念:Optional,Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
案例使用的员工类,这是后面案例中使用的员工类:
//学生类
package StreamCain;
public class Student {
private String name;
private String sex;
private int age;
private double soux;
//无参数
public Student(){}
//有参数构造方法
public Student(String name,String sex,int age,double soux){
this.name=name;
this.sex=sex;
this.age=age;
this.soux=soux;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSoux() {
return soux;
}
public void setSoux(double soux) {
this.soux = soux;
}
}
//老师类
package StreamCain;
import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.stream.Collectors;
public class Teacher {
public static void main(String[] args) {
Student cainfly = new Student("Cainfly", "男", 25, 198);
Student ss = new Student("ss", "女", 25, 176);
Student mm = new Student("mm", "男", 56, 156);
Student gg = new Student("gg", "女", 34, 136);
Student ff = new Student("ff", "女", 12, 98);
Student hh = new Student("hh", "男", 4, 77);
//将这些对象添加到集合中
ArrayListstudent = new ArrayList<>();
student.add(cainfly);
student.add(ss);
student.add(mm);
student.add(gg);
student.add(ff);
student.add(hh);
System.out.println(student);
Integer sunage = student.stream().collect(Collectors.summingInt(Student::getAge));
//::方法的引用
System.out.println(sunage);
}
}
3.1 遍历/匹配(foreach/find/match)Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
// 匹配第一个
OptionalfindFirst = list.stream().filter(x ->x >6).findFirst();
// 匹配任意(适用于并行流)
OptionalfindAny = list.parallelStream().filter(x ->x >6).findAny();
// 是否包含符合特定条件的元素booleananyMatch= list.stream().anyMatch(x ->x >6);
System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);
}
}
运行结果:
匹配第一个值:7
匹配任意一个值:8
是否存在大于6的值:true
list.forEach()与list.stream().forEach()区别
list.forEach()使用增强for循环。list.stream().forEach()首先将集合转换为流,然后对集合的流进行迭代。最后调用ReferencePipeline类的forEach方法。
publicvoidforEach(Consumer super E_OUT>action) {
if (!isParallel()) {
sourceStageSpliterator().forEachRemaining(action);
} else {
super.forEach(action);
}
}
forEachRemaining方法对集合中剩余的元素进行操作,也就是说只遍历一次集合元素。
当一边遍历一边删除的时候,forEach能够快速失败,而stream().forEach()只有等到数组遍历完之后才会抛异常。
筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
案例一:筛选出Integer集合中大于7的元素,并打印出来
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
Streamstream = list.stream();
stream.filter(x ->x >7).forEach(System.out::println);
}
}
预期结果:
8 9
3.3 聚合(max/min/count)max、min、count这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。
案例一:获取String集合中最长的元素。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
// 比较
Optionalmax = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());
}
}
输出结果:
最长的字符串:weoujgsd
案例二:获取Integer集合中的大值。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList(7, 6, 9, 4, 11, 6);
// 自然排序
Optionalmax = list.stream().max(Integer::compareTo);
// 自定义排序
Optionalmax2 = list.stream().max(newComparator() {
@Overridepublicintcompare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("自然排序的大值:" + max.get());
System.out.println("自定义排序的大值:" + max2.get());
}
}
输出结果:
自然排序的大值:11
自定义排序的大值:11
案例三:获取学生年龄最高的人。
Optionalmax = student.stream().max(Comparator.comparingInt(Student::getAge));
System.out.println(max.get().getAge());//56
案例四:计算Integer集合中大于6的元素的个数。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
longcount= list.stream().filter(x ->x >6).count();
System.out.println("list中大于6的元素个数:" + count);
}
}
输出结果:
list中大于6的元素个数:4
3.4 映射(map/flatMap)映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
案例一:英文字符串数组的元素全部改为大写。整数数组每个元素加3。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
String[] strArr = {"abcd", "bcdd", "defde", "fTr"};
// 数组元素转大写
ListstrList = Arrays.stream(strArr).map(String::toUpperCase)
.collect(Collectors.toList());
ListintList = Arrays.asList(1, 3, 5, 7, 9, 11);
// 元素加法
ListintListNew = intList.stream().map(x ->x + 3).collect(Collectors.toList());
System.out.println("每个元素大写:" + strList);
System.out.println("每个元素 + 3:" + intListNew);
}
}
输出结果:
每个元素大写:[ABCD, BCDD, DEFDE, FTR]
每个元素 + 3:[4, 6, 8, 10, 12, 14]
案例二:将员工的薪资全部增加1000。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
ListemployeeList = init();
// 不改变原来员工集合的方式
ListemployeeListNew = employeeList.stream().map(employee ->{
EmployeeemployeeNew=newEmployee(employee.getName(), 0, 0, null, null);
employeeNew.setSalary(employee.getSalary() + 10000);
return employeeNew;
}).collect(Collectors.toList());
System.out.println("一次改动前:" + employeeList.get(0).getName()
+ "-->" + employeeList.get(0).getSalary());
System.out.println("一次改动后:" + employeeListNew.get(0).getName()
+ "-->" + employeeListNew.get(0).getSalary());
// 改变原来员工集合的方式
ListemployeeListNew2 = employeeList.stream().map(employee ->{
employee.setSalary(employee.getSalary() + 10000);
return employee;
}).collect(Collectors.toList());
System.out.println("二次改动前:" + employeeList.get(0).getName()
+ "-->" + employeeListNew.get(0).getSalary());
System.out.println("二次改动后:" + employeeListNew2.get(0).getName()
+ "-->" + employeeListNew.get(0).getSalary());
}
}
输出结果:
一次改动前:Tom–>8900
一次改动后:Tom–>18900
二次改动前:Tom–>18900
二次改动后:Tom–>18900
案例三:将两个字符数组合并成一个新的字符数组。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList("m, k, l, a", "1, 3, 5, 7");
ListlistNew = list.stream().flatMap(s ->{
// 将每个元素转换成一个stream
String[] split = s.split(",");
Streams2 = Arrays.stream(split);
return s2;
}).collect(Collectors.toList());
System.out.println("处理前的集合:" + list);
System.out.println("处理后的集合:" + listNew);
}
}
输出结果:
处理前的集合:[m-k-l-a, 1-3-5-7]
处理后的集合:[m, k, l, a, 1, 3, 5, 7]
3.5 归约(reduce)归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
案例一:求Integer集合的元素之和、乘积和大值。
publicclassStreamTest {
publicstaticvoidmain(String[] args) {
Listlist = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求和方式1
Optionalsum = list.stream().reduce((x, y) ->x + y);
// 求和方式2
Optionalsum2 = list.stream().reduce(Integer::sum);
// 求和方式3Integersum3= list.stream().reduce(0, Integer::sum);
// 求乘积
Optionalproduct = list.stream().reduce((x, y) ->x * y);
// 求大值方式1
Optionalmax = list.stream().reduce((x, y) ->x >y ? x : y);
// 求大值写法2Integermax2= list.stream().reduce(1, Integer::max);
System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
System.out.println("list求积:" + product.get());
System.out.println("list求和:" + max.get() + "," + max2);
}
}
输出结果:
list求和:29,29,29
list求积:2112
list求和:11,11
案例二:求所有学生的工资之和
Double reduce1 = student.stream().map(Student::getSoux).reduce(0.0, Double::sum);
Optionalreduce = student.stream().map(Student::getSoux).reduce(Double::sum);
System.out.println(reduce1);//841.0
System.out.println(reduce.get().doubleValue());//841.0
3.6 收集(collect)collect(收集),可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。collect主要依赖java.util.stream.Collectors类内置的静态方法。
3.6.1 归集(toList/toSet/toMap)因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
下面用一个案例演示toList、toSet和toMap:
ArrayListobjects = new ArrayList<>();
objects.add("i");
objects.add("123");
objects.add("aa");
objects.add("vv");
objects.add("vv");
objects.add("you");
Listlist = objects.stream().collect(Collectors.toList());//转list
Setset = objects.stream().collect(Collectors.toSet());//转set
String joining = objects.stream().collect(Collectors.joining(","));//拼接
ArrayListnewList= objects.stream().collect(Collectors.toCollection(ArrayList::new));//不是转为了list或set,而是转为了指定的集合
System.out.println("list的结果为:"+list);
System.out.println("set的结果为:"+set);//唯一性
System.out.println("joining的结果为:"+joining);
System.out.println(newList);
运行结果:
list的结果为:[i, 123, aa, vv, vv, you]
set的结果为:[aa, vv, 123, i, you]
joining的结果为:i,123,aa,vv,vv,you
[i, 123, aa, vv, vv, you]
3.6.2 统计(count/averaging)Collectors提供了一系列用于数据统计的静态方法:
计数:count
平均值:averagingInt、averagingLong、averagingDouble
最值:maxBy、minBy
求和:summingInt、summingLong、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble
案例:统计学生人数、平均工资、年龄总合、最高工资。
Long count = student.stream().collect(Collectors.counting());//总数
Double aveng = student.stream().collect(Collectors.averagingDouble(Student::getSoux));//平均数
Integer age = student.stream().collect(Collectors.summingInt(Student::getAge));//年龄总和
Optionalmaxage = student.stream().map(Student::getAge).collect((Collectors.maxBy(Integer::compare)));//年龄的大值
Optionalmax = student.stream().max(Comparator.comparingInt(Student::getAge));//年龄的大值
System.out.println(count);//6
System.out.println(aveng);//140.16666666666666
System.out.println(age);//156
System.out.println(maxage.get().toString());//56
System.out.println(max.get().getAge());//56
3.6.3 接合(joining)joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
String name = student.stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println(name);//Cainfly,ss,mm,gg,ff,hh
Listlist = Arrays.asList("A", "B", "C");
Stringstring= list.stream().collect(Collectors.joining("-"));
System.out.println("拼接后的字符串:" + string);//A-B-C
}
}
3.6.4 归约(reducing)Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。
Double reduce1 = student.stream().map(Student::getSoux).reduce(0.0, Double::sum);
Optionalreduce = student.stream().map(Student::getSoux).reduce(Double::sum);
System.out.println(reduce1);//841.0
System.out.println(reduce.get().doubleValue());//841.0
3.7 排序(sorted)sorted,中间操作。有两种排序:
sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序
案例:将员工按工资由高到低(工资一样则按年龄由大到小)排序
// 按工资升序排序(自然排序)
Listcollect = student.stream().sorted(Comparator.comparing(Student::getSoux)).map(Student::getName).collect(Collectors.toList());
System.out.println(collect);
四、拓展
4.1 peek与map的区别Streampeek(Consumer super T>action)Streammap(Function super T, ? extends R>mapper);
peek接收一个Consumer,而map接收一个Function。
map:用于对流中的每个元素进行映射处理,然后再形成新的流;
peek:用于debug调试流中间结果,不能形成新的流,但能修改引用类型字段的值;
Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。
这就是为什么peek String不会发生变化而peek Object会发送变化的原因。
4.2 peek和foreach区别peek:会继续返回Stream对象
forEach:返回void,结束Stream操作。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前标题:Stream流整理-创新互联
网站路径:http://hbruida.cn/article/ejgid.html