Java8新特性学习随笔

本文详细介绍了Java8的新特性,包括Lambda表达式的语法、方法引用、接口的默认方法,深入探讨了函数接口的概念,并重点讲解了Stream API的数值流操作,如中间操作和最终操作。此外,还提到了Optional类和并行流的使用,展示了如何通过Stream高效地处理数据。
摘要由CSDN通过智能技术生成

Java8新特性随笔

Lambda
方法引用
默认方法
函数接口
Function
Stream

Lambda

“Lambda 表达式”(lambda expression)是一个匿名函数,代码简洁并且可读。Lambda表达式可以替代以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等。

Lambda语法,特征,案例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**特征**
 * 1. 类型声明(可选): 可以不需要声明类型,编译器会识别参数值
 * 2. 参数圆括号(可选):单个参数时,不需要圆括号。多个参数时,需要圆括号。
 * 3. 大括号和return关键字(可选):如果只有一个表达式,可以省略大括号和return关键字,编译器会*自动的返回值;在使用大括号的情况下,则必须指明返回值
 *
 *(parameters) -> expression
 *(parameters) -> { statements;
 */
//案例
public class LambdaExDemo {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三",21));
        list.add(new Student("李四",15));
        list.add(new Student("王五",23));
        list.add(new Student("赵六",32));

        System.out.println("排序前:"+list);

        //第一种 传统匿名Compartor接口排序
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge().compareTo(o2.getAge());
            }
        });
        System.out.println("匿名接口方法排序后: "+list);
        //使用Lambda表达式接口来代替匿名接口方法
        //声明式,不使用大括号,只写单条语句
        Collections.sort(list,(Student s1,Student s2)->s1.getAge().compareTo(s2.getAge()));
        System.out.println("声明式,不使用大括号,只写单条语句: "+list);
        //不声明式,使用大括号,可以写多条语句
        Collections.sort(list,(a,b)->{
            System.out.println("------------------ : "+a.getAge().compareTo(b.getAge()));
            System.out.println("a.Age: "+a.getAge()+" b.Age: "+b.getAge());
            return a.getAge().compareTo(b.getAge());
        });
        System.out.println();
        System.out.println("不声明式,使用大括号,可以写多条语句: "+list);

        //使用Lambda表达式调用静态方法
        Collections.sort(list,(a,b)->Student.sortByName(a,b));
        System.out.println("使用Lambda表达式调用静态方法: "+list);

        //使用Lambda表达式调用类的实现
        Collections.sort(list,(a,b)->new Student().sortByAge(a,b));
        System.out.println("使用Lambda表达式调用类的实现: "+list);
    }
}
Lambda 方法引用种类及案例
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class LambdaExDemo01 {
    /**
     * 方法引用的种类:
     * 1.类静态方法的引用
     * 2.某个对象的方法的引用
     * 3.特定类的任意对象的方法的引用
     * 4.构造方法的引用
     */
    public static void main(String[] args) {
        List<Student> list = Student.getSomeStuddent();
        //静态方法的引用
        Collections.sort(list,Student::sortByName);
        System.out.println("静态方法的引用: "+list);

        //对象方法的使用
        Collections.sort(list,new Student()::sortByAge);
        System.out.println("类方法的使用: "+list);

        //特定类方法的调用
        Integer [] s = new Integer[]{5,1,3,6,7,8,2};
        Arrays.sort(s,Integer::compare);
        System.out.println("特定类方法的调用: "+Arrays.toString(s));

        //引用类的构造器
        Student student = Student.create(Student::new);
        System.out.println("引用类的构造器 : "+student.toString());
    }
}
接口添加默认/静态方法

在Java8以前,未接口添加一个通用的实现是很不容易的,接口一旦发布就等于是定型,如果这个时候在接口内增加一个方法,那么就会破坏所有实现接口的对象。
默认方法(虚拟扩展方法或守护方法)的目标就是解决这个问题,使得接口在发布之后任然能够逐步演化。

public interface SmallStudent {
    /**
     * 静态方法和默认方法均可以有多个,默认方法可以被覆盖
     */
    
    //默认方法
    default void print(){
        System.out.println("我是一名小学生");
    }
    //静态方法
    static void sing(){
        System.out.println("小学生在说话!");
    }
}
函数接口

“函数接口”,就是除去默认方法以及继承的抽象的方法,只有显示声明一个抽象的接口。它使用@Functionallnterface注解在类上进行标注,也可以省略。Java会自动识别。以下是一些常用案例。

java.util.Comparator
Comparator是Java中经典接口,在排序中较为常用。Java8在此之上新增了一些新的默认方法

		public static void main(String[] args) {
        Integer [] abs = new Integer[]{3,5,8,7,6};
        Comparator<Integer> comparator = Integer::compare;
        Arrays.sort(abs,comparator);
        System.out.println("ASC:"+Arrays.toString(abs));
        Arrays.sort(abs,comparator.reversed());
        System.out.println("DESC:"+Arrays.toString(abs));
    }

java.util.function.Predicate
该接口包含方法boolean test(T t),一般用于条件的检测,内部包含三个默认方法:and(与),or(或),negate(非)用于各式的判断。

public static void main(String[] args) {
		//notice : 在这里与或非的判断顺序是从左到右的,调用的顺序会影响结果。
        Predicate<Integer> predicate = a -> a > 5;
        predicate.test(10);
        System.out.println(predicate.test(10)); //true

        predicate.negate().test(10);
        System.out.println(predicate.negate().test(10));//false

        predicate.or(a -> a < 1).and(a -> a > -1).negate().test(-1);
        System.out.println( predicate.or(a -> a < 1).and(a -> a > -1).negate().test(-1));//true
        }

java.util.function.Supplier
此类只有一个方法:T get();
Supplier接口是在1.8中新出现的函数接口,用于支持函数是编程,它用于返回一个任意泛型的实力对象,与工厂功能类似。

java.util.function.Consumer
该接口表示一个接受单个输入参数并且没有返回值的操作。不像其他函数式接口,Consumer接口期望执行修改内容的操作。例如,我们需要一个批量修改student的方法,利用Predicate和Consumer可以完成。

 public static List updateBatch(List<Student> students, Predicate<Student> predicate, Consumer<Student> consumer){
        for (int i = 0; i <students.size() ; i++) {
            if(predicate.test(students.get(i))){
                consumer.accept(students.get(i));
            }
        }
        return  students;
    }

测试

 public static void main(String[] args) {

        //批量修改年龄小于25的均改为25
        List<Student> list = Student.getSomeStuddent();
        System.out.println(Student.updateBatch(list,s -> s.getAge() < 25,s -> s.setAge(25)));
        List<String > students =  Student.updateBatch(list,s -> s.getAge() < 25,s -> s.setAge(25));
        System.out.println(students );
        System.out.println(list);
        }

Function
Java8提供的java.util.function包的核心函数接口有4个:

  1. 函数型 T -> R ,完成参数类型T向结果类型R的转换和数据处理。核心函数接口Function
  2. 判断型 T -> boolean,核心函数接口Predicate
  3. 消费型 T ->void, 核心函数接口Consumer
  4. 供给型 void -> T ,核心函数接口Supplier

Function接口是为Java8提供了函数式编程的基础,apply方法与Consumer的accept方法功能类似。但是提供了返回及类型转换的可能。功能更加强大;再通过andThen与compose方法可以使Function组成Function功能链,进行多级数据处理及转换。

主要方法

  1. R apply(T t) – 将Function对象引用到输入的参数上,然后返回计算结果。
  2. default Function<T,V>andThen(Function<? super R,? extends V > after)返回一个先执行当前函数对象apply 方法再执行after函数对象apply方法的函数对象。
  3. defaullt Function<T,V> compose(Function<? super R,? extends V > before) 返回一个先执行before函数对想apply方法再执行当前函数对象apply方法的函数对象。
  4. staticc Function<T,T>identity() 返回一个执行了apply()方法之后会返回输入参数的函数对象。

详解
apply:

R apply(T t);

接收类型:T
返回类型:R
类型转换:T -> R
Function 接口的核心方法,可以执行任意的操作,且具有返回值。接收一个T对象,在经过处理后,返回一个R类型的对象。主要功能为类型转换及数据处理。

compose:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

接收类型:Function<? super V, ? extends T>
返回类型:Function<V, R>
类型转换:(V+)->(T-)->T->R
apply执行顺序:before -->this
此处“V+”指代“? super V”,表示包含V在内的V的任意父类;"T-"指代“? extends T”,表示包含T在内的T的任意子类。compose方法返回一个Function<V,R>,这个Function先执行before的apply方法,将V+类型的数据转换为T-类型,再将T-作为参数传递给this的apply方法,将T类型转换为R类型。
通过compose方法,可以在某个Function执行之前插入一个Function执行。由于返回类型依旧为Function,可以重复调用compose方法形成方法链。

andThen:

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

接收类型:Function<? super R, ? extends T>
返回类型:Function<T, V>
类型转换:T->R->(R+)->(V-)
apply执行顺序:this–>after
此处“R+”指代“? super R”,表示包含R在内的R的任意父类;"V-"指代“? extends V”,表示包含V在内的V的任意子类。andThen方法返回一个Function<T,V>,这个Function先执行this的apply方法,将T类型的数据转换为R类型,再将R作为参数传递给after的apply方法,将R+类型转换为V-类型。
通过andThen方法,可以在某个Function执行之后插入一个Function执行。由于返回类型依旧为Function,可以重复调用andThen方法形成方法链。

identity:

    static <T> Function<T, T> identity() {
        return t -> t;
    }

接收类型:无
返回类型:Function<T, T>
类型转换:T→T

该方法的说明是:返回一个函数,它总是返回输入参数。调用该方法可以得到一个返回输入参数的Funtion,这个Function就可以单纯的用来做数据处理,而不用类型转换。

Stream
java8中提供的Stream API,即流式处理,可以将List,Set,Array等对象转换成流进行操作。Stream内的流操作分为两种,中间操作与最终操作,中间操作会返回一个全新的Stream对象,意味着你的操作不会影响最初的流;最终操作会将流进行转换或者操作,返回非Stream的对象。Stream可以替代传统的循环操作,从线程上的区别,Stream分为穿行(Stream)和并行(parallelStream)。

Stream内一般方法:
中间操作:

  • distinct
  • skip
  • limit
  • filter
  • map
  • flatMap
  • sorted
  • peek
//distinct
//去除Stream中重复的对象,并返回一个流。(使用对象的equals方法)
//这个方法是通过类的 equals() 方法来判断两个元素是否相等的
java Stream<T> distinct();

//skip
//跳过Stream中的前n个对象,将其他对象返回一个Stream.如果n超过了Stream中对象的个数,则会返回一个空的Stream
java Stream<T> skip(long n)

//limit
//截取Stream的前maxSize个对象,并形成一个新Stream
java Stream<T> limit(long maxSize);
    public static void main(String[] args) {
        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);
        //返回前2个元素
        list = list.stream().limit(2).collect(Collectors.toList());
        System.out.println(list);
    }
//filter
//根据给定的predicate来过滤对象,返回满足条件的对象构成Stream
Stream<T> filter(Predicate<? super T> predicate);
//案例
 public static void main(String[] args) {
       //保留年龄为25的Student
        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);
        list = list.stream().filter(student -> student.getAge() == 25).collect(Collectors.toList());
        System.out.println(list);

    }
    //skip  跳过指定的几个
public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);

        //用在limit之前,先去除前m个元素再返回剩余元素的前n个元素,也就是先得到两个再跳过一个
        list = list.stream().limit(2).skip(1).collect(Collectors.toList());
        }
//map
//通过给定的mapper,将T类型的流转换为R类型的Stream。 类似与类型转换
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
 public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);
        List<String> names = list.stream().map(Student::getName).collect(Collectors.toList());
        System.out.println(names);
        }
//flatMap
//flatMap也是将Stream进行转换,flatMap与map的区别在于将一个Stream中的每个值都转成一个个Stream,然后再将这些流扁平化成为一个Stream。
 <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
public static void main(String[] args) {
//        List<Student> list = Student.getSomeStuddent();
        List<String> list = new ArrayList<>();
        list.add("aaa bbb ccc");
        list.add("ddd eee fff");
        list.add("ggg hhh iii");
        System.out.println(list);
        //将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
        //例子中,我们的目的是把 List 中每个字符串元素以" "分割开,变成一个新的 List
        list = list.stream().map(s->s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
        System.out.println(list);
        }
//sorted
//sorted方法可以对Stream进行排序。排序的对象必须实现Comparable,如果没实现会抛出ClassCastException;不提供comparator时,则会调用compareTo方法。
java Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
//根据年龄来比较大小
public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);
        //根据年龄来比较大小 升序
        list = list.stream().sorted((s1,s2)-> s1.getAge() - s2.getAge()).collect(Collectors.toList());
        //根据年龄来比较大小 升序降序
        list = list.stream().sorted((s1,s2)-> s2.getAge() - s1.getAge()).collect(Collectors.toList());
        //可以进行简化
        list = list.stream().sorted(Comparator.comparingInt(Student::getAge)).collect(Collectors.toList());
        list = list.stream().sorted(Comparator.comparingInt(Student::getAge).reversed()).collect(Collectors.toList());
        System.out.println(list);
    }
    
//peek
//对流中的每个对象执行提供的action操作。在Stack中,peek用于查看一个对象。在流中也是一样,用于在流循环时,根据给定的action进行查看对象。虽然可以进行元素修改操作,但不建议。
java Stream<T> peek(Consumer<? super T> action);

最终操作:

  • 聚合
  • 匹配
//聚合
//max & min
//根据给定的comparator返回Stream中的max或min。
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
//count
//返回Stream中对象的个数。
long count();
public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);

        Long length = list.stream().count();
        System.out.println(length);
        }

//匹配
//anyMatch & allMatch & noneMatch
//根据给定的predicate判断Stream是否匹配条件。
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
//anyMatch,allMatch,noneMatch
public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);
        //是否有一个匹配给定的条件
        boolean res = list.stream().anyMatch(student -> student.getAge()==25);
        System.out.println(res);
        //是否左右匹配给定的条件
        boolean res1 = list.stream().allMatch(student -> student.getAge()==25);
        System.out.println(res1);
        //是否没有元素匹配给定的条件
        boolean res2 = list.stream().noneMatch(student -> student.getAge()==25);
        System.out.println(res2);
        }
//collect
//根据给定的collector对Stream中的元素进行操作,返回复杂数据结构的对象。用于将Stream中的对象转换成我们想要的结构,如list、map、set等。前例中就使用collect(Collectors.toList())将Stream中的对象转换成List。
<R, A> R collect(Collector<? super T, A, R> collector);

//reduce
//如果我们不知希望单纯的返回List这样的类型,而是希望将整个Stream经过一些操作后,规约成一个对象返回,就可以用到规约操作。reduce方法有两个参数,其中accumulator代表着规约的操作,即用何种的方式进行参数化处理;identity则是accumulator的标识值。
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);

        //reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
        //用于组合流中的元素,如求和,求积,求最大值等
        //计算年龄之和
        //其中,reduce 第一个参数 0 代表起始值为 0,lambda (a, b) -> a + b 即将两值相加产生一个新值
        Integer ageSum = list.stream().map(student -> student.getAge()).reduce(0,(a,b)->a+b);
        System.out.println(ageSum);
        Integer ageSum1 = list.stream().map(Student::getAge).reduce(0,(a,b)->a+b);
        System.out.println(ageSum1);
        //即不接受任何起始值,但因为没有初始值,需要考虑结果可能不存在的情况,因此返回的是 Optional 类型
        Optional<Integer> sums = list.stream().map(Student::getAge).reduce(new Student()::sum);
        System.out.println(sums.get());
}

//toArray
//将Stream中的对象返回成一个Object数组。
Object[] toArray();

//forEach
//顾名思义,对Stream中每个元素进行action操作,与peek类似,但forEach是一个最终操作,一般在结束时查看对象使用。
void forEach(Consumer<? super T> action);

//findFirst & findAny
//indFirst可以返回Stream中第一个对象,并将它封装在Optional中。findAny则不是返回第一个对象,而是任意一个对象。在顺序Stream中findFirst和findAny的结果是一致的,但在并行Stream中,findFirst存在着限制,故在并行Stream中需要使用findAny(findAny源码注释中写的是some element?)。同样将对象封装在Optional中。

Optional<T> findFirst();
Optional<T> findAny();
Stream 数值流:
  1. 流与数值流的转换
  2. 流转换为数值流
  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream
 public static void main(String[] args) {

        List<Student> list = Student.getSomeStuddent();
        System.out.println(list);

        IntStream intStream1 = list.stream().mapToInt(Student::getAge);
        Stream<Integer> streams = intStream1.boxed();
        System.out.println(streams.collect(Collectors.toList()));


    }
  1. 数值流转换为流
    boxed()
  2. 数值流方法
  • sum()
  • max()
  • average()
  • min()
  1. 数值范围
    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 的数值流

求 1 到 10 的数值总和:

public static void main(String[] args) {
        IntStream intStream = IntStream.rangeClosed(1, 10);
        int sum = intStream.sum();
        System.out.println(sum);
  1. Optional 类
    NullPointerException 可以说是每一个 Java 程序员都非常讨厌看到的一个词,针对这个问题, Java 8 引入了一个新的容器类 Optional,可以代表一个值存在或不存在,这样就不用返回容易出问题的 null。
    Optional 类比较常用的几个方法有:
    isPresent() :值存在时返回 true,反之 flase
    get() :返回当前值,若值不存在会抛出异常
    orElse(T) :值存在时返回该值,否则返回 T 的值
    Optional 类还有三个特化版本 OptionalInt,OptionalLong,OptionalDouble,刚刚讲到的数值流中的 max 方法返回的类型便是这个
  2. 构建流
    之前我们得到一个流是通过一个原始数据源转换而来,其实我们还可以直接构建得到流。
    值创建流
    Stream.of(T…) : Stream.of(“aa”, “bb”) 生成流
    生成一个字符串流
    Stream stream = Stream.of(“aaa”, “bbb”, “ccc”);
    Stream.empty() : 生成空流

数组创建流
根据参数的数组类型创建对应的流:
Arrays.stream(T[ ])
Arrays.stream(int[ ])
Arrays.stream(double[ ])
Arrays.stream(long[ ])
值得注意的是,还可以规定只取数组的某部分,用到的是Arrays.stream(T[], int, int)

 public static void main(String[] args) {
        //只取索引第 1 到第 2 位的
        int[] a = {1, 2, 3, 4};
        Arrays.stream(a, 1, 3).forEach(System.out :: println);
        }
        //打印 2 ,3

文件生成流
Stream stream = Files.lines(Paths.get(“data.txt”));
每个元素是给定文件的其中一行
函数生成流
两个方法:
iterate : 依次对每个新生成的值应用函数
generate :接受一个函数,生成一个新的值

public static void main(String[] args) {
//        生成流,首元素为 0,之后依次加 2
        Stream.iterate(0, n -> n + 2);
//        生成流,为 0 到 1 的随机双精度数
        Stream.generate(Math :: random);
//        生成流,元素全为 1
        Stream.generate(() -> 1);
        }
  1. collect 收集数据
    coollect 方法作为终端操作,接受的是一个 Collector 接口参数,能对数据进行一些收集归总操作
    收集
    最常用的方法,把流中所有元素收集到一个 List, Set 或 Collection 中
    toList
    toSet
    toCollection
 public static void main(String[] args) {
        List<Student> list = Student.getSomeStuddent();
        List<Student> newlist = list.stream().collect(Collectors.toList());
        }

汇总
(1)counting
用于计算总和:
long l = list.stream().collect(counting());
也可以:
long l = list.stream().count();
推荐第二种

(2)summingInt ,summingLong ,summingDouble
summing,计算总和,不过这里需要一个函数参数
计算 Person 年龄总和:
int sum = list.stream().collect(summingInt(Person::getAge));
当然,这个可以也简化为:
int sum = list.stream().mapToInt(Person::getAge).sum();
除了上面两种,其实还可以:
int sum = list.stream().map(Person::getAge).reduce(Interger::sum).get();
推荐第二种
由此可见,函数式编程通常提供了多种方式来完成同一种操作

(3)averagingInt,averagingLong,averagingDouble
看名字就知道,求平均数
Double average = list.stream().collect(averagingInt(Person::getAge));
当然也可以这样写
OptionalDouble average = list.stream().mapToInt(Person::getAge).average();
不过要注意的是,这两种返回的值是不同类型的

(4)summarizingInt,summarizingLong,summarizingDouble
这三个方法比较特殊,比如 summarizingInt 会返回 IntSummaryStatistics 类型
IntSummaryStatistics l = list.stream().collect(summarizingInt(Person::getAge));
IntSummaryStatistics 包含了计算出来的**平均值,总数,总和,最值,**可以通过下面这些方法获得相应的数据

取最值
maxBy,minBy 两个方法,需要一个 Comparator 接口作为参数
Optional optional = list.stream().collect(maxBy(comparing(Person::getAge)));
我们也可以直接使用 max 方法获得同样的结果
Optional optional = list.stream().max(comparing(Person::getAge));

joining 连接字符串
也是一个比较常用的方法,对流里面的字符串元素进行连接,其底层实现用的是专门用于字符串连接的 StringBuilder
String s = list.stream().map(Person::getName).collect(joining());
结果:jackmiketom

String s = list.stream().map(Person::getName).collect(joining(","));
结果:jack,mike,tom

joining 还有一个比较特别的重载方法:
String s = list.stream().map(Person::getName).collect(joining(" and ", "Today “, " play games.”));
结果:Today jack and mike and tom play games.
即 Today 放开头,play games. 放结尾,and 在中间连接各个字符串

groupingBy 分组
groupingBy 用于将数据分组,最终返回一个 Map 类型

Map<Integer, List> map = list.stream().collect(groupingBy(Person::getAge));
例子中我们按照年龄 age 分组,每一个 Person 对象中年龄相同的归为一组

另外可以看出,Person::getAge 决定 Map 的键(Integer 类型),list 类型决定 Map 的值(List

多级分组
groupingBy 可以接受一个第二参数实现多级分组:

Map<Integer, Map<T, List>> map = list.stream().collect(groupingBy(Person::getAge, groupBy(…)));
其中返回的 Map 键为 Integer 类型,值为 Map<T, List

按组收集数据
Map<Integer, Integer> map = list.stream().collect(groupingBy(Person::getAge, summingInt(Person::getAge)));
该例子中,我们通过年龄进行分组,然后 summingInt(Person::getAge)) 分别计算每一组的年龄总和(Integer),最终返回一个 Map<Integer, Integer>
根据这个方法,我们可以知道,前面我们写的:
groupingBy(Person::getAge)
其实等同于:
groupingBy(Person::getAge, toList())
partitioningBy 分区
分区与分组的区别在于,分区是按照 true 和 false 来分的,因此partitioningBy 接受的参数的 lambda 也是 T -> boolean
根据年龄是否小于等于20来分区
Map<Boolean, List> map = list.stream()
.collect(partitioningBy(p -> p.getAge() <= 20));
打印输出
{
false=[Person{name=‘mike’, age=25}, Person{name=‘tom’, age=30}],
true=[Person{name=‘jack’, age=20}]
}
同样地 partitioningBy 也可以添加一个收集器作为第二参数,进行类似 groupBy 的多重分区等等操作。

并行

之前我就讲到了 parallelStream 方法能生成并行流,因此你通常可以使用 parallelStream 来代替 stream 方法,但是并行的性能问题非常值得我们思考

比方说下面这个例子

int i = Stream.iterate(1, a -> a + 1).limit(100).parallel().reduce(0, Integer::sum);
我们通过这样一行代码来计算 1 到 100 的所有数的和,我们使用了 parallel 来实现并行。

但实际上是,这样的计算,效率是非常低的,比不使用并行还低!一方面是因为装箱问题,这个前面也提到过,就不再赘述,还有一方面就是 iterate 方法很难把这些数分成多个独立块来并行执行,因此无形之中降低了效率。

流的可分解性
这就说到流的可分解性问题了,使用并行的时候,我们要注意流背后的数据结构是否易于分解。比如众所周知的 ArrayList 和 LinkedList,明显前者在分解方面占优。

作者: 一个计算机菜鸟
出处: https://www.cnblogs.com/qbzf-Blog/p/8946963.html#_caption_6
出处: https://cloud.tencent.com/developer/article/1187833
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值