JDK1.8新特性——Stream

Posted weixin_42412601

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8新特性——Stream相关的知识,希望对你有一定的参考价值。

目录


流定义:流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合。对集合的操作简单了。

生成流

  1. 集合创建串行流 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类还提供了minmax等一系列数值操作,当流中元素为数值类型时可以直接使用。

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,sumint;Double,Long类似

数值范围

IntStreamLongStream 拥有 rangerangeClosed 方法用于数值范围处理。

  • IntStreamrangeClosed(int, int) / range(int, int)
  • LongStreamrangeClosed(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, SetCollection

  • 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的主要内容,如果未能解决你的问题,请参考以下文章

010-jdk1.8版本新特性二-Optional类,Stream流

jdk1.8新特性——Stream(流)的创建

jdk1.8新特性Stream流式处理

JDK1.8新特性——Stream

jdk1.8新特性——Stream(流)的终止操作基本语法

jdk1.8新特性——Stream(流)的中间操作基本语法