当前位置: 首页 > news >正文

Java8 特性(一):函数、Lambok、Stream

函数接口-Functional Inteface

自定义函数接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。

@FunctionalInterface
public interface MyFunctionInterface {

    /**
     * 定义的抽象方法
     * 基本格式:public abstract 返回值 方法名称(参数列表)
     * public abstract 可以不写,编译器自动加上
     */
    void method();

    /**
     * 默认方法
     * 自定义的人方式实现
     */
    default void defaultMethod(String str) {
        System.out.println("这是自定义的默认方式实现");
        method();
        System.out.println("输入的参数为:" + str);
    }
}

一旦使用@FunctionalInterface该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样(该接口是一个标记接口)。

public class TestFunctional1 {

    public static void main(String[] args) {
        // 方式一:调用函数式接口的方法
        doSomthing(() -> System.out.println("excuter lambda!"));

        // 方式二
        MyFunctionInterface functionInterface = () -> System.out.println("传入的是我自定义的函数内容,这里的函数内容,仅仅只是打印");
        functionInterface.method();

        // 调用默认实现
        MyFunctionInterface functionInterface1 = () -> System.out.println("自定义函数2");
        functionInterface1.defaultMethod("哈哈");
    }

    // 定义一个含有函数式接口的方法
    public static void doSomthing(MyFunctionInterface functionInterface) {
        // 调用自定义函数式接口的方法
        functionInterface.method();
    }
}

打印结果为:

excuter lambda!
传入的是我自定义的函数内容,这里的函数内容,仅仅只是打印
这是自定义的默认方式实现
自定义函数2
输入的参数为:哈哈

Java 提供的函数接口

JDK 1.8 之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口:

  • java.util.function

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

序号接口 & 描述
1BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果
2BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果
3BinaryOperator
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4BiPredicate<T,U>
代表了一个两个参数的boolean值方法
5BooleanSupplier
代表了boolean值结果的提供方
6Consumer
代表了接受一个输入参数并且无返回的操作
7DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。
9DoubleFunction
代表接受一个double值参数的方法,并且返回结果
10DoublePredicate
代表一个拥有double值参数的boolean值方法
11DoubleSupplier
代表一个double值结构的提供方
12DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。
13DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果
14DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。
15Function<T,R>
接受一个输入参数,返回一个结果。
16IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。
17IntConsumer
接受一个int类型的输入参数,无返回值 。
18IntFunction
接受一个int类型输入参数,返回一个结果 。
19IntPredicate
接受一个int输入参数,返回一个布尔值的结果。
20IntSupplier
无参数,返回一个int类型结果。
21IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。
22IntToLongFunction
接受一个int类型输入,返回一个long类型结果。
23IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。
24LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。
25LongConsumer
接受一个long类型的输入参数,无返回值。
26LongFunction
接受一个long类型输入参数,返回一个结果。
27LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。
28LongSupplier
无参数,返回一个结果long类型的值。
29LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。
30LongToIntFunction
接受一个long类型输入,返回一个int类型结果。
31LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。
32ObjDoubleConsumer
接受一个object类型和一个double类型的输入参数,无返回值。
33ObjIntConsumer
接受一个object类型和一个int类型的输入参数,无返回值。
34ObjLongConsumer
接受一个object类型和一个long类型的输入参数,无返回值。
35Predicate
接受一个输入参数,返回一个布尔值结果。
36Supplier
无参数,返回一个结果。
37ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果
38ToDoubleFunction
接受一个输入参数,返回一个double类型结果
39ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。
40ToIntFunction
接受一个输入参数,返回一个int类型结果。
41ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。
42ToLongFunction
接受一个输入参数,返回一个long类型结果。
43UnaryOperator
接受一个参数为类型T,返回值类型也为T。

Predicate 接口

Predicate 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。该接口包含多种默认方法(default方法)来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。该接口主要用于测试对象是 true 或 false。

public class TestFunctional2 {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        System.out.println("输出所有数据:");
        // 传递参数 n,自定义函数逻辑为始终返回true
        eval(list, n -> true);
        System.out.println("输出所有偶数:");
        // 传递参数 n,自定义函数逻辑为n%2 == 0时,返回true
        eval(list, n -> n % 2 == 0);
    }

    public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.test(n)) {
                System.out.println(n + " ");
            }
        }
    }
}

Supplier 接口

java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

public class TestFunctional3 {

    public static void main(String[] args) {
        Supplier<String> supplier = () -> "自定义返回的数据";
        System.out.println(supplier.get());
        // 强转后获取数据
        System.out.println(((Supplier<String>) () -> {
            System.out.println("这里打印一句话");
            return "产生数据";
        }).get());

        // 求数组元素最大值
        getMax();
    }

    private static void getMax() {
        Integer[] data = new Integer[]{6, 5, 4, 3, 2, 1};
        Supplier<Integer> supplierInt = () -> {
            int max = 0;
            for (int i = 0; i < data.length; i++) {
                max = Math.max(max, data[i]);
            }
            return max;
        };
        System.out.println("数组元素最大值为:" + supplierInt.get());
    }
}

Consumer 接口

java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

public class TestFunctional4 {

    public static void main(String[] args) {
        Consumer<String> consumer = (s) -> {
            System.out.println("自定义函数逻辑为打印参数:" + s);
        };
        consumer.accept("OK");
    }
}

Function<T, R> 接口

java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件

public class TestFunctional5 {

    public static void main(String[] args) {
        Function<Number, String> function = (number) -> {
            System.out.println("这里会把Number转成String");
            return String.valueOf(number);
        };
        String apply = function.apply(100);
        System.out.println(apply);
    }
}


Lambok 表达式

Java 引入 Lambda 表达式的主要作用是简化代码编写(免去了使用匿名方法的麻烦)。实际上,Lambda 表达式在 Java 中只是一个语法糖而已,底层是基于函数接口来实现的。

Lambda 表达式包括三部分:输入函数体输出。表示出来的话就是下面这个样子:

// a,b是输入参数
(a, b) -> { 语句1; 语句2...; return 输出; } 

实际上,Lambda 表达式的写法非常灵活。刚刚给出的是标准写法,还有很多简化写法。比如,如果输入参数只有一个,可以省略 (),直接写成 a -> {…};如果没有入参,可以直接将输入和箭头都省略掉,只保留函数体;如果函数体只有一个语句,那可以将{}省略掉;如果函数没有返回值,return 语句就可以不用写了。

上述描述其实就是Lambok表达式的特征,也可被总结如下:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

(s) -> System.out.println(s)和 (String s) -> System.out.println(s)是一样的编译器会进行类型推断,所以不需要添加参数类型。

  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  1. s -> System.out.println(s) 一个参数不需要添加圆括号;
  2. (x, y) -> Integer.compare(y, x) 两个参数就需要添加了圆括号,否则编译器报错。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  1. s -> System.out.println(s) , 不需要大括号;
  2. (s) -> { if (s.equals(“s”)){ System.out.println(s); } }; 需要大括号
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。


Stream 类

介绍

Stream 优点

​ Stream 是 Java 8 提供的新功能,是对集合(Collection)对象功能的增强,能对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation);与Lambda 表达式结合,也可以提高编程效率、简洁性和程序可读性;同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程;通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。

什么是Stream

​ Stream流其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值),Stream是一个来自数据源的元素队列。它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

需要注意的是:Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

 java.util.Stream 表示了某一种元素的序列,在这些元素上可以进行各种的操作。其中,stream 操作可以是中间操作,也可以是结束操作。中间操作会返回流对象本身,你可以通过多次调用同一个流的操作方法,来将操作结果串起来;结束操作会返回一个某种类型的值。

 Stream 是在一个源的基础上创建出来的,如:java.util.Collection中的List或者Set(Map 不能作为 Stream 的源)。其执行方式可以分为【串行】和【并行】这两种方式。

public interface Stream<T> extends BaseStream<T, Stream<T>> {
}

Stream 构造方法

Stream 内置的构造方法:


Collection 声明的 Stream 函数:

public interface Collection<E> extends Iterable<E> {
 		
  	/**
     * 返回以此集合为源的顺序Stream 。
     * 当spliterator()方法无法返回 IMMUTABLE 、 CONCURRENT或后期绑定的IMMUTABLE时,应该重写此方法。
     */
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    /**
     * 返回以该集合为源的可能并行Stream 。此方法允许返回顺序流。
     * 当spliterator()方法无法返回 IMMUTABLE 、 CONCURRENT或后期绑定的IMMUTABLE时,应该重写此方法。
     */
    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

Collection声明了stream转化函数,也就是说,任意Collection子类都存在官方替我们实现的由Collection转为Stream的方法。

基本使用

1、创建流

1.1 使用Collection下的 stream() 和 parallelStream() 方法

public static void main(String[] args) {
  List<String> list = new ArrayList<>();
  // 获取一个顺序流
  Stream<String> stream = list.stream();
  // 获取一个并行流
  Stream<String> parallelStream = list.parallelStream();
}

1.2 使用Arrays 中的 stream() 方法,将数组转成流

public static void main(String[] args) {
  Integer[] nums = new Integer[10];
  Stream<Integer> stream = Arrays.stream(nums);
}

1.3 使用Stream中的静态方法:of()、iterate()、generate()

public static void main(String[] args) {
  Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

  Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
  stream2.forEach(System.out::println); // 0 2 4 6 8 10

  Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
  stream3.forEach(System.out::println);
}

1.4 使用 BufferedReader.lines() 方法,将每行内容转成流

public static void main(String[] args) throws FileNotFoundException {
  BufferedReader reader = new BufferedReader(new FileReader("/Users/xxx/test_stream.txt"));
  Stream<String> lineStream = reader.lines();
  lineStream.forEach(System.out::println);

  Pattern pattern = Pattern.compile(",");
  // 使用 Pattern.splitAsStream() 方法,将字符串分隔成流
  Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
  stringStream.forEach(System.out::println);
}

2、流的中间操作

2.1 筛选与切片

  • filter:过滤流中的某些元素
  • limit(n):获取n个元素
  • skip(n):跳过n元素,配合limit(n)可实现分页
  • distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

在这里插入图片描述

public static void main(String[] args) {
  Stream<Integer> stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
  // 6 4 7 3 9 8 10 12 14
  stream.distinct()
    // 7 3 9 8 10 12 14
    .skip(2)
    // 7 3
    .limit(2)
    .forEach(System.out::println);

  // 再次操作流stream会报错:stream has already been operated upon or closed
  Stream<Integer> stream1 = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
  // 6 6 7 9 8 10 12 14 14
  Stream<Integer> newStream = stream1.filter(s -> s > 5);
  newStream.forEach(System.out::println);
}

2.2 映射

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

在这里插入图片描述
在这里插入图片描述

public static void main(String[] args) {
  List<String> list = Arrays.asList("a,b,c", "1,2,3");

  // 将每个元素转成一个新的且不带逗号的元素
  Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
  s1.forEach(System.out::println); // abc  123

  Stream<String> s3 = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
  });
  s3.forEach(System.out::println); // a b c 1 2 3
}

2.3 排序

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):定制排序,自定义Comparator排序器
public static void main(String[] args) {
  List<String> list = Arrays.asList("aa", "ff", "dd");
  //String 类自身已实现Compareable接口
  list.stream().sorted().forEach(System.out::println);// aa dd ff

  Student s1 = new Student("aa", 10);
  Student s2 = new Student("bb", 20);
  Student s3 = new Student("aa", 30);
  Student s4 = new Student("dd", 40);
  List<Student> studentList = Arrays.asList(s1, s2, s3, s4);

  //自定义排序:先按姓名升序,姓名相同则按年龄升序
  studentList.stream().sorted(
    (o1, o2) -> {
      if (o1.getName().equals(o2.getName())) {
        return o1.getAge() - o2.getAge();
      } else {
        return o1.getName().compareTo(o2.getName());
      }
    }
  ).forEach(System.out::println);
}

@Data
static class Student {
  private String name;
  private Integer age;

  public Student(String name, Integer age) {
    this.name = name;
    this.age = age;
  }
}

2.4 消费

  • peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
public static void main(String[] args) {
  Student s1 = new Student("aa", 10);
  Student s2 = new Student("bb", 20);
  List<Student> studentList = Arrays.asList(s1, s2);

  studentList.stream()
    .peek(o -> o.setAge(100))
    .forEach(System.out::println);
}

@Data
static class Student {
  private String name;
  private Integer age;

  public Student(String name, Integer age) {
    this.name = name;
    this.age = age;
  }
}

3、流的终止操作

3.1 匹配、聚合操作

  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  • findFirst:返回流中第一个元素
  • findAny:返回流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中元素最大值
  • min:返回流中元素最小值
public static void main(String[] args) {
  List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

  boolean allMatch = list.stream().allMatch(e -> e > 10);
  System.out.println(allMatch);   // false
  boolean noneMatch = list.stream().noneMatch(e -> e > 10);
  System.out.println(noneMatch);   // true
  boolean anyMatch = list.stream().anyMatch(e -> e > 4);
  System.out.println(anyMatch);   // true

  Integer findFirst = list.stream().findFirst().get();
  System.out.println(findFirst);   // 1
  Integer findAny = list.stream().findAny().get();
  System.out.println(findAny);   // 1

  long count = list.stream().count();
  System.out.println(count);   // 5
  Integer max = list.stream().max(Integer::compareTo).get();
  System.out.println(max);   // 5
  Integer min = list.stream().min(Integer::compareTo).get();
  System.out.println(min);   // 1
}

3.2 规约操作

  • Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
  • T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
  • U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner):
    • 在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用
    • 在并行流(parallelStream)中,流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。
    public static void main(String[] args) {
        //经过测试,当元素个数小于24时,并行时线程数等于元素个数,当大于等于24时,并行时线程数为16
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24);

        Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get();
        System.out.println(v);   // 300

        Integer v1 = list.stream().reduce(10, (x1, x2) -> x1 + x2);
        System.out.println(v1);  // 310

        Integer v2 = list.stream().reduce(0,
                (x1, x2) -> {
                    System.out.println("stream accumulator: x1:" + x1 + "  x2:" + x2);
                    return x1 - x2;
                },
                (x1, x2) -> {
                    System.out.println("stream combiner: x1:" + x1 + "  x2:" + x2);
                    return x1 * x2;
                });
        System.out.println(v2); // -300

        Integer v3 = list.parallelStream().reduce(0,
                (x1, x2) -> {
                    System.out.println("parallelStream accumulator: x1:" + x1 + "  x2:" + x2);
                    return x1 - x2;
                },
                (x1, x2) -> {
                    System.out.println("parallelStream combiner: x1:" + x1 + "  x2:" + x2);
                    return x1 * x2;
                });
        System.out.println(v3); //-775946240
    }

3.3 收集操作

collect:接收一个Collector实例,将流中元素收集成另外一个数据结构

Collector<T, A, R> 是一个接口,有以下5个抽象方法:

  • Supplier<A> supplier():创建一个结果容器A。
  • BiConsumer<A, T> accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T。
  • BinaryOperator<A> combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
  • Function<A, R> finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
  • Set<Characteristics> characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
    • CONCURRENT:表示此收集器支持并发。(官方文档还有其他描述,暂时没去探索,故不作过多翻译)
    • UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
    • IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。

Collector 工具库:Collectors

    public static void main(String[] args) {
        Student s1 = new Student("aa", 10, 1);
        Student s2 = new Student("bb", 20, 2);
        Student s3 = new Student("cc", 10, 3);
        List<Student> list = Arrays.asList(s1, s2, s3);

        //转成list
        List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList());
        // 结果为:[10, 20, 10]

        // 转成set
        Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet());
        // 结果为:[20, 10]

        // 转成map,注意:key不能相同,否则报错
        Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
        // 结果为:{cc=10, bb=20, aa=10}

        //字符串分隔符连接
        String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")"));
        // 结果为:(aa,bb,cc)

        //聚合操作
        //1.学生总数
        Long count = list.stream().collect(Collectors.counting());
        // 结果为:3
        //2.最大年龄 (最小的minBy同理)
        Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();
        // 结果为:20
        //3.所有人的年龄
        Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge));
        // 结果为:40
        //4.平均年龄
        Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge));
        // 结果为:13.333333333333334
        // 带上以上所有方法
        DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
        System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());

        //分组
        Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
        //多重分组,先根据类型分再根据年龄分
        Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream()
                .collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));

        //分区
        //分成两部分,一部分大于10岁,一部分小于等于10岁
        Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));

        //规约
        Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();
        // 结果为:40
        System.out.println(1);
    }

    @Data
    static class Student {
        private String name;
        private Integer age;
        private Integer type;

        public Student(String name, Integer age, Integer type) {
            this.name = name;
            this.age = age;
            this.type = type;
        }
    }

相关文章:

  • 定时器(Quartz)
  • 神经网络实现线性回归,神经网络是回归算法吗
  • MFC调用VLC库播放中文路径导致崩溃的问题
  • 微信公众号搜题功能接口
  • 5.java不同方法的区别(构造方法,实例方法,类方法,static关键字)
  • 无胁科技-TVD每日漏洞情报-2022-8-31
  • 力扣-221题 最大正方形(C++)- dp
  • 信号量(信号灯) -----//目的:实现共享内存的多个进程之间同步
  • JS解决contenteditable=“true“的光标位置放到最后
  • 使用Qt的WebSocket模块小常识
  • 前端ES5,ES6模块Demo
  • 2022/08/31 吉软 JSP的基本使用
  • Nginx--Rewrite重写
  • vue——响应式数据、双向数据绑定、filter过滤器、面试题
  • MindMaster思维导图及亿图图示会员 超值获取途径
  • canvas 绘制双线技巧
  • iOS 系统授权开发
  • JAVA之继承和多态
  • JS专题之继承
  • Linux各目录及每个目录的详细介绍
  • mysql中InnoDB引擎中页的概念
  • NSTimer学习笔记
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • select2 取值 遍历 设置默认值
  • Sublime text 3 3103 注册码
  • Vue.js-Day01
  • vue-loader 源码解析系列之 selector
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 构造函数(constructor)与原型链(prototype)关系
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 我这样减少了26.5M Java内存!
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • ​马来语翻译中文去哪比较好?
  • #pragma data_seg 共享数据区(转)
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (39)STM32——FLASH闪存
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (二十三)Flask之高频面试点
  • (转)原始图像数据和PDF中的图像数据
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .dwp和.webpart的区别
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .NET连接MongoDB数据库实例教程
  • @Bean注解详解
  • @Transient注解
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • [2016.7 day.5] T2
  • [4.9福建四校联考]
  • [AIGC 大数据基础]hive浅谈
  • [C++]打开新世界的大门之C++入门
  • [CISCN 2023 初赛]go_session
  • [Contest20180313]灵大会议
  • [hdu 3065] 病毒侵袭持续中 [AC自动机] [病毒特征码匹配]