JDK1.8新特性——Stream
Posted weixin_42412601
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8新特性——Stream相关的知识,希望对你有一定的参考价值。
目录
- 生成流
- 遍历流 forEach()
- 映射流map()
- 过滤流 filter()
- 流的特点
- 流统计count()
- 限制流limit()
- skip(n) 跳过前n个元素
- limit()+skip()实现分页
- 组合流concat()
- 流排序 sorted()
- 去重distinct()
- 将多个stream 合并到一个Stream flatMap()
- anyMatch() 是否匹配任一元素
- allMatch() 是否匹配所有元素
- noneMatch() 是否未匹配所有元素
- findAny()找到其中一个元素
- findFirst() 获取第一个元素
- 归约 reduce()能实现归约
- collect()收集器
- 并行程序parallelStream()是流并行处理程序的代替方法
- 附录
流定义:流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合。对集合的操作简单了。
生成流
- 集合创建串行流 stream()
List<String> strings = Arrays.asList("abc","","bc","efg","abcd","","jkl");
//过滤流里元素""并把过滤后的stream转成集合
List<String> collects = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
2.为数组生成流对象
String[] names="a","b","c";
Stream<String> stream = Arrays.stream(names);
3.直接将几个值变成流对象 Stream.of()
Stream<String> abc = Stream.of("abc", "hha", "hehh");
4.文件变成流
Stream<String> lines = Files.lines(Paths.get("文件路径"), Charset.defaultCharset());
遍历流 forEach()
jdk8集合是怎么遍历的:
List<String> strings = Arrays.asList("abc","","bc","efg","abcd","","jkl");
List<String> collects = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("遍历集合:");
//lamdba方式
collects.forEach(collect-> System.out.println(collect+" "););
//方法引用方式
collects.forEach(System.out::println);
遍历流:
void forEach(Consumer<? super T> action)
;该方法接受一个Consumer
函数式接口,可以传递lamdba
表达式,进行消费数据
List<String> strings = Arrays.asList("abc","","bc","efg","abcd","","jkl");
System.out.println("遍历流:");
strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::print);
映射流map()
用于映射每个元素到对应的结果 。
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
; Function
可以将一种T
类型转换成R
类型,这种转换动作,称为映射。
List<Integer> numbers=Arrays.asList(1,2,3,4);
List<Integer> collect = numbers.stream().map(number ->
number++;
number = number * 2;
return number;
).collect(Collectors.toList());
collect.forEach(System.out::println);
过滤流 filter()
用于通过设置的条件过滤出元素, Stream<T> filter(Predicate<? super T> predicate)
;
Stream.of("张三","李四","王五").filter(string ->string.startsWith("张")).forEach(System.out::print);
多条件:也可以使用多个filer
List<KfCollectPO> collect = kfCollectPOS.stream()
.filter(kfCollectPO -> resourceTypeSet.equals(kfCollectPO.getResourceType()) && kfCollectPO.getResourceId() == kfDataSetPO.getSetId())
.collect(Collectors.toList());
流的特点
Stream流属于管道流,只能被消费一次。第一个流调用完毕就会流转到下一个流上,而这时第一个流已经使用完毕,会自动关闭。
Stream<String> stringStream= Stream.of("张三", "李四", "王五");
Stream<String> stringStream1 = stringStream.filter(string -> string.startsWith("张"));
stringStream1.forEach(System.out::print);
//第一个流已经关闭,在使用会报错:java.lang.IllegalStateException: stream has already been operated upon or closed
stringStream.forEach(System.out::print);
流统计count()
用于统计Stream
中元素的个数
Stream<String> stringStream= Stream.of("张三", "李四", "王五");
long count = stringStream.count();
System.out.println(count);
限制流limit()
用于获取指定数量的流
Stream<String> stringStream= Stream.of("张三", "李四", "王五");
stringStream.limit(2).forEach(System.out::print);
skip(n) 跳过前n个元素
Stream<String> stringStream= Stream.of("张三", "李四", "王五");
stringStream.skip(2).forEach(System.out::print);
limit()+skip()实现分页
int pageSize=3;
int pageNo=2;
Stream<String> stringStream= Stream.of("张三", "李四", "王五","李六","逍遥子","杨过");
stringStream.skip(pageSize * (pageNo-1)).limit(pageSize).forEach(System.out::print);
组合流concat()
用于将两个流合并成一个流
Stream<String> stringStream1= Stream.of("张三", "李四", "王五");
Stream<String> stringStream2= Stream.of("李六","逍遥子","杨过");
Stream.concat(stringStream1,stringStream2 ).forEach(System.out::print);
流排序 sorted()
用于对流进行排序
List<Integer> str4=Arrays.asList(4,3,2,123);
str4.stream().sorted().forEach(System.out::println);
//倒叙
List<Integer> str4=Arrays.asList(4,3,2,123);
str4.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
自定义排序规则:
//按照abcd排序
Stream.of("b","a","d","c","e","f").sorted(Comparator.comparing(String::hashCode)).forEach(System.out::print);
//倒序
Stream.of("b","a","d","c","e","f").sorted(Comparator.comparing(String::hashCode).reversed()).forEach(System.out::print);
//对集合排序,排序规则是元素的名字字段
list.stream().sorted(Comparator.comparing(Student::getAge));
Comparator
comparing
方法入参为Function
函数式接口,说明可以传一个lamdba
表达式,可以使用方法引用优化,。
底层实际上使用的是compareTo
方法进行比较,因为Function
的出参继承自U
,而U
继承自Comparable
接口,所以能使用compareTo
方法
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
去重distinct()
List<String> str3 = Arrays.asList("abc", "", "abc", "efg", "abcd","", "jkl");
List<String> collect = str3.stream().distinct().collect(Collectors.toList());
collect.forEach(System.out::println);
将多个stream 合并到一个Stream flatMap()
flatMap
参数:Function<? super T, ? extends Stream<? extends R>
;
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
list.stream().map(s -> s.split(" ")).flatMap(string->
Stream<String> stream = Arrays.stream(string);
return stream;
).forEach(System.out::print);
分析:map
把集合中的每一个元素按照" "分割并返回一个String
数组,map
完成后,流的类型变成Stream<String[]>
,flatMap
先使用Array.stream
将传进来的每个String[]
转成Stream<String>
流,然后就将这些流连接成一个流并返回得到一个完整的Stream<String>
。
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
//生成Stream<String[]>,里面包含三个String[]
//[aaa, bbb, ccc]
//[ddd, eee, fff]
//[ggg, hhh, iii]
Stream<String[]> stream1 = list.stream().map(s -> s.split(" "));
//flatMap遍历生成Stream,每个元素都是一个String[]
//里面的逻辑是,把数组转成Stream<String>
//然后通过flatMap的遍历,把三个String[]生成的Stream<String>,都拼成一个Stream<String>
stream1.flatMap(string->
//Arrays.stream可以为数组输出成流对象
Stream<String> stream = Arrays.stream(string);
return stream;
).forEach(System.out::print);
anyMatch() 是否匹配任一元素
流中是否至少有一个元素匹配给定的 T -> boolean
条件,有一个或多个元素满足条件,返回true
,没有一个元素满足条件,返回false
String[] words=new String[]"hello","world";
boolean b = Arrays.stream(words).anyMatch(word ->
if ("hello".equals(word))
return true;
return false;
);
System.out.println(b);
true
allMatch() 是否匹配所有元素
流中是否所有元素都匹配给定的 T -> boolean
条件,全都匹配,返回true
,否则,返回false
String[] words=new String[]"hello","world";
boolean b = Arrays.stream(words).allMatch(word ->
if ("hello".equals(word))
return true;
return false;
);
System.out.println(b);
false
String[] words=new String[]"hello","hello";
boolean b = Arrays.stream(words).allMatch(word ->
if ("hello".equals(word))
return true;
return false;
);
System.out.println(b);
true
noneMatch() 是否未匹配所有元素
流中是否没有元素匹配给定的 T -> boolean
条件,没有一个元素匹配,返回true
,有一个元素匹配,返回fasle
。
String[] words=new String[]"hello","world";
boolean b = Arrays.stream(words).noneMatch(word ->"123".equals(word));
System.out.println(b);
true
String[] words=new String[]"hello","world";
boolean b = Arrays.stream(words).noneMatch(word ->"hello".equals(word));
System.out.println(b);
false
findAny()找到其中一个元素
找到其中一个元素 (使用 stream()
时找到的是第一个元素;使用 parallelStream()
并行时找到的是其中一个元素)
返回一个Optional
类型的元素
Optional介绍
List<String> str3 = Arrays.asList("abc", "", "abc", "efg", "abcd","", "jkl");
Optional<String> optionalS = str3.stream().findAny();
System.out.println(optionalS.get());
Optional<String> any = str3.parallelStream().findAny();
System.out.println(any.get());
abc
abcd
findFirst() 获取第一个元素
找到第一个元素
List<String> str3 = Arrays.asList("abc", "", "abc", "efg", "abcd","", "jkl");
Optional<String> optionalS = str3.stream().findFirst();
System.out.println(optionalS.get());
Optional<String> any = str3.parallelStream().findFirst();
System.out.println(any.get());
abc
abc
归约 reduce()能实现归约
reduce
函数接收两个参数:1.初始值 2.进行归约操作的Lambda
表达式
自定义Lambda
表达式实现规约操作,如果当前流的元素为数值类型,那么可以使用Integer
提供的sum
函数代替自定义的Lambda
表达式,Integer
类还提供了min
、max
等一系列数值操作,当流中元素为数值类型时可以直接使用。
List<Person> list=new ArrayList<>();
list.add(new Person("张三",24));
list.add(new Person("李四",26));
list.add(new Person("王五",28));
//求和 0表示初始值
int sum1 = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);
int sum2 = list.stream().map(Person::getAge).reduce(0, Integer::sum);
System.out.println("和:"+sum1);
System.out.println("和:"+sum2);
//求积
Integer reduce = list.stream().map(Person::getAge).reduce(1, (a, b) -> a * b);
System.out.println("积:"+reduce);
//最大值
Optional<Integer> reduce1 = list.stream().map(Person::getAge).reduce(Integer::max);
System.out.println("最大值:"+reduce1.get());
reduce
的第一个参数表示初试值
reduce
的第二个参数为需要进行的归约操作,它接收一个拥有两个参数的Lambda
表达式,reduce
会把流中的元素两两输给Lambda
表达式,最后将计算出来。
reduce()第一个参数是上次累计的和,第二个参数是数据流的下一个元素
数值流的使用
采用reduce
进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低。 当流操作为纯数值操作时,使用数值流能获得较高的效率。
StreamAPI
提供了三种数值流:IntStream、DoubleStream、LongStream
将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong
数值流转成普通流:boxed()
;
将串行数值流转成并行数值流:parallel()
每种数值流都提供了数值计算函数,如max、min、sum、average
等。
OptionalDouble average = list.stream().map(Person::getAge).mapToInt(Integer::intValue).average();
System.out.println(average.getAsDouble());
max,min
返回结果为OptionalInt,sum
为int
;Double,Long
类似
数值范围
IntStream
与 LongStream
拥有 range
和 rangeClosed
方法用于数值范围处理。
IntStream
:rangeClosed(int, int) / range(int, int)
LongStream
:rangeClosed(long, long) / range(long, long)
这两个方法的区别在于一个是闭区间,一个是半开半闭区间:
rangeClosed(1, 100)
:[1, 100]
range(1, 100)
:[1, 100)
我们可以利用 IntStream.rangeClosed(1, 100) 生成 1 到 100 的数值流:
long sum = LongStream.rangeClosed(0L, 100).sum();
collect()收集器
collect
方法作为终端操作,接受的是一个 Collector
接口参数,能对数据进行一些收集归总操作
收集
最常用的方法,把流中所有元素收集到一个 List
, Set
或 Collection
中
- toList
- toSet
- toCollection
- toMap
public static void main(String[] args)
List<Person> data = new ArrayList<>();
Person p1=new Person("张三",1);
Person p2=new Person("李四",2);
Person p3=new Person("王五",3);
data.add(p1);
data.add(p2);
data.add(p3);
//toMap 第一个参数为key 第二个参数为value
Map<String, Integer> collect = data.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
// Map<String, Integer> collect = data.stream().collect(Collectors.toMap(item->item.getName(), item->item.getAge()));
System.out.println(collect);
// 李四=2, 张三=1, 王五=3
// 李四=2, 张三=1, 王五=3
// 李四=2, 张三=1, 王五=3
@AllArgsConstructor
@Data
static class Person
private String name;
private int age;
汇总
counting计数
用于计算数据的数量:
List<Integer> numbers=Arrays.asList(1,2,3,4);
Long collect = numbers.stream().collect(Collectors.counting());
System.out.println(collect);
4
//也可以不使用收集器的计数器
long count = numbers.stream().count();
推荐第二种
summingInt ,summingLong ,summingDouble
summing
,没错,也是计算总和,不过这里需要一个函数参数
List<Person> list=new ArrayList<>();
list.add(new Person("张三",24));
list.add(new Person("李四",26));
list.add(new Person("王五",28));
//使用收集器的规约操作
Integer collect = list.stream().collect(Collectors.summingInt(Person::getAge));
System.out.println(collect);
//使用reduce的规约操作
Integer reduce = list.stream().map(Person::getAge).reduce(0, Integer::sum);
System.out.println(reduce);
//使用数值流的规约操作
int sum = list.stream().mapToInt(Person::getAge).sum();
System.out.println(sum);
三种规约操作,函数式编程通常提供了多种方式来完成同一种操作,推荐使用数值流。
averagingInt,averagingLong,averagingDouble
List<Person> list=new ArrayList<>();
list.add(new Person("张三",24));
list.add(new Person("李四",26));
list.add(new Person("王五",28));
//使用收集器的规约操作
Double collect = list.stream().collect(Collectors.averagingInt(Person::getAge));
System.out.println(collect);
//使用数值流的规约操作
double asDouble = list.stream().mapToInt(Person::getAge).average().getAsDouble();
System.out.println(asDouble);
summarizingInt,summarizingLong,summarizingDouble
这三个方法比较特殊,比如 summarizingInt
会返回 IntSummaryStatistics
类型
IntSummaryStatistics collect = list.stream().collect(Collectors.summarizingInt(Person::getAge));
System.out.println("最大值:"+collect.getMax());
System.out.println("最小值:"+collect.getMin());
System.out.println("和:"+collect.getSum());
System.out.println("平均值:"+collect.getAverage());
System.out.println("总数:"+collect.getCount());
IntSummaryStatistics
包含了计算出来的平均值,总数,总和,最值。
取最值
maxBy,minBy
两个方法,需要一个 Comparator
接口作为参数
Optional<Person> collect = list.stream().collect(Collectors.maxBy(Comparator.comparing(Person::getAge)));
System.out.println(collect.get().getAge());
//也可以直接使用 max 方法获得同样的结果
Optional<Person> max = list.stream().max(Comparator.comparing(Person::getAge));
System.out.println(max.get().getAge());
joining
连接字符串
对流里面的字符串元素进行连接,其底层实现用的是专门用于字符串连接的 StringBuilder
。
String collect = list.stream().map(Person::getName).collect(Collectors.joining());
System.out.println(collect);
结果:张三李四王五
String collect = list.stream().map(Person::getName).collect(Collectors.joining(":"));
System.out.println(collect);
结果:张三:李四:王五
//第一个参数是连接符,第二个参数是前缀,第三个参数是后缀
String collect = list.stream().map(Person::getName).collect(Collectors.joining(":","开始:"," 结束"));
System.out.println(collect);
开始:张三:李四:王五 结束
归约操作
若你需要自定义一个归约操作,那么需要使用Collectors.reducing
函数,该函数接收三个参数:
- 第一个参数为归约的初始值
- 第二个参数为归约操作进行的字段
- 第三个参数为归约操作的过程
例子一:
List<Integer> numbers=Arrays.asList(1,2,3,4);
Integer collect = numbers.stream().collect(Collectors.reducing(0, Integer::intValue, (a, b) -> a + b));
System.out.println(collect);
例子二:
Optional<Integer> sumAge = list.stream()
.collect(Collectors.reducing(0,Person::getAge,(i,j)->i+j));
第三个参数表示归约的过程。这个参数接收一个Lambda
表达式,而且这个Lambda
表达式一定拥有两个参数,分别表示当前相邻的两个元素。由于我们需要累加,因此我们只需将相邻的两个元素加起来即可。
groupingBy
分组
https://blog.csdn.net/zhouzhiwengang/article/details/112319054
分组就是将流中的元素按照指定类别进行划分,类似于SQL
语句中的GROUPBY
。
Map<Integer, List<Person>> collect = list.stream().collect(Collectors.groupingBy(Person::getAge));
collect.forEach((k,v)->
System.out.println(k);
v.stream().forEach(a->
System.out.println(a.getName()+" "+a.getAge());
);
);
一级分组
Map<String, List<Teacher>> collect = teachers.stream().collect(Collectors.groupingBy(teacher ->
if (teacher.getAge() > 60)
ret以上是关于JDK1.8新特性——Stream的主要内容,如果未能解决你的问题,请参考以下文章