Lambda表达式与流式编程

一、Lambda表达式

1.1 什么是lambda表达式

Lambda表达式是 jdk1.8 引入的一个新特性,它是函数式编程在Java中的一种体现。也是1.8最值得学习的新特性。

  1. Lambda表达式实际上就是匿名内部类的简化版本。

  2. Lambda表达式是jdk1.8引入的一个最重要的新特性,另外一个就是集合的流式编程。

  3. Lambda表达式是java为了扩展函数式编程引入的。

  4. Lambda表达式也可以理解为是一个匿名函数(匿名方法:方法没有名字)。

  5. Lambda表达式只能作用于函数式接口(有且只有一个抽象方法的接口)。

1.2 Lambda基础语法

1.2.1 语法解析

(parameters) -> expression

或者

(parameters) -> { statements;}

解析:

箭头(->)将参数与Lambda主体分隔开来。

参数部分:1.参数可以是任意合法的Java参数列表,可以为空或包含一个或多个参数。

2.参数列表的类型名可以省略。 不能出现有些省略了,有些没有省略的情况。

3.如果参数列表中,参数的数量有且只有一个,则小括号可以省略。

Lambda主体:

1.Lambda主体可以是一个表达式,表达式外的大括号,可加可不加。 没有大括号时,return关键字必须省略。

2.也可以是一个代码块。将按照常规的Java语法执行,并且您可能需要使用return语句来返回值。

1.2.2 语法案例

1.无参数的Lambda表达式:

java 复制代码
() -> System.out.println("Hello, Lambda!");
  1. 带有参数的表达式:
java 复制代码
一个参数:
(int m) -> System.out.println(m);
或
(m) -> System.out.println(m);
或
 m  -> System.out.println(m);
多个参数:
(int x, int y) -> System.out.println(x + y);
或者
(x, y) -> System.out.println(x + y);
  1. 带有多行代码的Lambda表达式:
java 复制代码
(x, y) -> {
    int sum = x + y;
    System.out.println("Sum: " + sum);
    return sum;
}

1.3 Lambda的应用

1.3.1 lambda的应用场景

lambda表达式,只能作用于函数式接口。

函数式接口:就是有且只有一个抽象方法的接口。

1.3.2 案例演示

下面是一些函数式接口:

java 复制代码
//里面的抽象方法,没有形参,没有返回值。
interface NoParameterNoReturn{
    void Print();
}
//里面的抽象方法,只有一个形参,没有返回值。
interface OneParameterNoReturn{
    void Print(String info);
}
//里面的抽象方法,有多个形参,没有返回值。
interface MuilParameterNoReturn{
    void Print(String info,int age);
}
//里面的抽象方法,没有形参,有返回值。
interface NoParameterReturn{
    int Calculate();
}
//里面的抽象方法,只有一个形参,有返回值。
interface OneParameterReturn{
    int Calculate(int a);
}
//里面的抽象方法,有多个形参,有返回值。
@FunctionalInterface //注解是用来校验是否为函数式接口。
interface MuilParameterReturn{
    int Calculate(int a,int b);
}
java 复制代码
public class _02LambdaDemo01 implements NoParameterNoReturn{
    public static void main(String[] args){
        /*2. 测试实现类*/
        _02LambdaDemo01 obj = new _02LambdaDemo01();
        obj.Print();
        /*3. 使用匿名内部类的方式,实现NoParameterNoReturn接口,打印"我一定能学会java",并测试*/
        NoParameterNoReturn npnr = new NoParameterNoReturn() {
            public void Print(){
                System.out.println("我一定能学会java");
            }
        };
        npnr.Print();
        /*4. 使用Lambda表达式的方式,实现NoParameterNoReturn接口,打印"哈哈哈,我哭了...",并测试*/
        NoParameterNoReturn npnr1 = () -> System.out.println("哈哈哈,我哭了...");
        npnr1.Print();
        /*5. 使用Lambda表达式的方式,实现OneParameterNoReturn接口,打印"'我喜欢'+形参",并测试打印,传入'苹果'*/
        OneParameterNoReturn opnr = (info) -> System.out.println("我喜欢"+info);//一个参数时,形参小括号可以省略
        opnr.Print("苹果");
        /*6. 使用Lambda表达式的方式,实现MuilParameterNoReturn接口,打印两个参数拼接的效果,并测试,传入"我今年","18"*/
        MuilParameterNoReturn mpnr = (info,age) -> System.out.println(info+age);
        mpnr.Print("我今年",18);
        /*7. 使用Lambda表达式的方式,实现NoParameterReturn接口,计算两个随机数,区间[25,40]的和,并测试*/
        // NoParameterReturn npr = ()-> ((int)(Math.random()*16)+25+(int)(Math.random()*16)+25));
        NoParameterReturn npr = ()-> {
            int a = (int)(Math.random()*16)+25;
            int b = (int)(Math.random()*16)+25;
            System.out.println(a);
            System.out.println(b);
            return a+b;
        };
        System.out.println(npr.Calculate());
        /*8. 使用Lambda表达式的方式,实现OneParameterReturn接口,计算形参的立方,并测试传入3*/
        OneParameterReturn opr = (a) -> {
            int sum = (int)(Math.pow(a,3));
            return sum;
        };
        System.out.println(opr.Calculate(3));
        /*9. 使用Lambda表达式的方式,实现MuilParameterReturn接口,计算两个形参的立方和,并测试传入3和4*/
        MuilParameterReturn mpr = (a1,b1) -> {
            int sum = a1*a1*a1+b1*b1*b1;
            return sum;
        };
        System.out.println(mpr.Calculate(3,4));
    }
    /*1.使用实现类的方式,实现NoParameterNoReturn接口,打印"java编程真简单"*/
    @Override
    public void Print() {
        System.out.println("java编程真简单");
    }
}

1.4 变量的捕获

变量的捕获:在内部对外部的变量的引用和访问。

1.4.1 匿名内部类的变量捕获

在Java中,匿名内部类可以捕获外部变量,即在匿名内部类中引用并访问外部作用域的变量。这种行为称为变量捕获(Variable Capturing)。

在匿名内部类中,可以捕获以下类型的变量:

  1. 实例变量(成员变量,属性,全局变量):

访问外部类的成员变量:外部类名.this.成员变量 或者直接写

  1. 静态变量:访问外部类的静态变量:外部类名.静态变量 或者直接写

  2. 方法形参:匿名内部类访问的方法形参,只能访问,不能覆盖。

  3. 本地变量(局部变量):匿名内部类访问的局部变量是默认被final修饰的;final修饰的变量只能初始化一次。

匿名内部类访问上述四个变量时,对局部变量(方法形参,本地变量)只能访问,不能覆盖。

java 复制代码
public class _01InnerClassDemo {
    private int a;//实例变量
    private static int b;//静态变量
    static{
        b = 2;
    }
    public _01InnerClassDemo(){
        a = 1;
    }
    public void m1(int c){
        int d = 4;//本地变量(局部变量)
        MyTest mt = new MyTest(){
            @Override
            public void test1() {
                //访问外部类的成员变量:外部类名.this.成员变量   或者直接写
                System.out.println("instance variable:  "+a);
                //访问外部类的静态变量:外部类名.静态变量   或者直接写
                System.out.println("static variable:  "+b);
                //匿名内部类访问的方法形参,只能访问,不能覆盖。
                System.out.println("method variable:  "+c);
                //匿名内部类访问的局部变量是默认被final修饰的;final修饰的变量只能初始化一次。
                System.out.println("native variable:  "+d);
            }
        };
        mt.test1();
    }
    public static void main(String[] args) {
        //测试:创建外部类对象,调用m1方法。
        _01InnerClassDemo c1 = new _01InnerClassDemo();
        c1.m1(3);
    }
}
interface MyTest{
    void test1();
}

1.4.2 Lambda表达式的变量捕获

在Lambda表达式中,同样可以捕获外部作用域的变量。Lambda表达式可以捕获以下类型的变量:

  1. 实例变量(成员变量,属性,全局变量):

访问外部类的成员变量:外部类名.this.成员变量 或者直接写

  1. 静态变量:访问外部类的静态变量:外部类名.静态变量 或者直接写

  2. 方法形参:访问的方法形参,只能访问,不能覆盖。

  3. 本地变量(局部变量):访问的局部变量是默认被final修饰的;final修饰的变量只能初始化一次。

注意:lambda表达式也是在访问上述四种变量时,对局部变量(方法形参,本地变量)只能访问,不能覆盖。

java 复制代码
public class _01InnerClassDemo {
    private int a;//实例变量
    private static int b;//静态变量
    static{
        b = 2;
    }
    public _01InnerClassDemo(){
        a = 1;
    }
    public void m2(int c){
        int d = 4;
        MyTest mt = () -> {
            //访问外部类的成员变量:外部类名.this.成员变量   或者直接写
            System.out.println("instance variable:  "+a);
            //访问外部类的静态变量:外部类名.静态变量   或者直接写
            System.out.println("static variable:  "+b);
            //访问的方法形参,只能访问,不能覆盖。
            System.out.println("method variable:  "+c);
            //访问的局部变量是默认被final修饰的;final修饰的变量只能初始化一次。
            System.out.println("native variable:  "+d);
        };
        mt.test1();
    }
    public static void main(String[] args) {
        //测试:创建外部类对象,调用m1方法。
        _01InnerClassDemo c1 = new _01InnerClassDemo();
        c1.m2(3);
    }
}
interface MyTest{
    void test1();
}

1.5 Lambda表达式在集合中的应用

1) 排序时,使用比较器时。

java 复制代码
List<String> list = new ArrayList<String>();
list.add("michael");
list.add("david");
list.add("bob");
list.add("lucy");
//按照字符串的长度降序:比较器使用了lambda表达式的方法。
Collections.sort(list,(a,b)->b.length()-a.length());
System.out.println(list);
运行结果:
[michael, david, lucy, bob]

2)forEach迭代元素

forEach(Consumer c)的源码:

for(T t:this){

c.accept()

}

Consumer是一个函数式接口:里面只有一个抽象方法 void accept(T t)

因此只需要向forEach()方法中传入accept的匿名函数,也就是lambda表示即可。

  1. List集合的迭代
java 复制代码
Integer[] arr = new Integer[]{4,5,10,7,2};
List<Integer> nums = Arrays.asList(arr);
for (Integer num : nums) {
    System.out.println(num);
}
nums.forEach(num -> System.out.println(num));
//继续简化
nums.forEach(System.out::println);
  1. Set集合的迭代
java 复制代码
Integer[] arr = new Integer[]{4,5,10,7,2};
List<Integer> nums = Arrays.asList(arr);
Set<Integer> set = new HashSet<>(nums);
set.forEach(System.out::println);
  1. Map的迭代
java 复制代码
Map<String, Integer> map = new HashMap<>();
map.put("张三", 18);
map.put("李四", 19);
map.put("王五", 17);
map.put("赵六", 20);
//key的迭代
map.keySet().forEach(key -> System.out.println(key));
//entrySet的迭代
map.entrySet().forEach(entry -> System.out.println(entry.getKey()+"-->"+entry.getValue()));
//values的迭代
map.values().forEach(value -> System.out.println(value));

3)根据条件移除元素

removeIf(Predicate filter): 满足过滤条件就会删除。

源码解析: 内部逻辑就是一个迭代器遍历集合,根据条件做删除操作, 条件就是filter的test方法。 Predicate是一个函数式接口,里面有boolean test(T t)方法,因此我们在使用时就是写一个lambda表达式来实现test方法即可。

java 复制代码
List<Integer> ages = Arrays.asList(18,19,17,20,17);
List<Integer> ages2 = new ArrayList<>(ages);
ages2.removeIf(m -> m.equals(17));
System.out.println(ages2);

1.6 Lambda表达式的优缺点

Lambda表达式在Java中引入了函数式编程的概念,具有许多优点和一些限制。下面是Lambda表达式的主要优点和缺点。

1)优点:

  1. 简洁性:Lambda表达式提供了一种更简洁、更紧凑的语法,可以减少冗余的代码和样板代码,使代码更易于理解和维护。

  2. 代码可读性:Lambda表达式使得代码更加自解释和易读,可以直接将逻辑集中在一起,提高代码的可读性和可维护性。

  3. 便于并行处理:Lambda表达式与Java 8引入的Stream API结合使用,可以方便地进行集合的并行处理,充分发挥多核处理器的优势,提高代码的执行效率。

  4. 避免匿名内部类的繁琐语法:相比于使用匿名内部类,Lambda表达式的语法更为简洁,减少了冗余的代码,提高了编码效率。

2)缺点:

  1. 只能用于函数式接口:Lambda表达式只能用于函数式接口(只有一个抽象方法的接口),这限制了它的使用范围。如果需要使用非函数式接口,仍然需要使用传统的方式,如匿名内部类。

  2. 可读性的折衷:尽管Lambda表达式可以提高代码的可读性,但在某些复杂的情况下,Lambda表达式可能变得难以理解和阅读,特别是当表达式变得过于复杂时。

  3. 变量捕获的限制:Lambda表达式对捕获的变量有一些限制。它们只能引用final或实际上的最终变量,这可能对某些情况下的代码编写和调试带来一些困扰。

  4. 学习曲线:对于习惯于传统Java编程风格的开发者来说,Lambda表达式是一项新的概念,需要一定的学习和适应过程。

二、集合的流式编程

2.1 流式编程的简介

流式编程是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一。(另外一个是 lambda表达式)。

Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储的。流更像是 一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。

2.2 为什么要使用集合的流式编程

1.传统方式,如果对集合中的元素做处理时,可能要书写大量的代码,比如增加,删除,过滤等。

2.集合的流式编程是对传统方式的一种简化操作。

2.3 使用流式编程的步骤

集合的流式编程,分三步:

--第一步: 获取数据源(关联数据源),返回Stream对象。

--第二步: 对Stream对象进行各种处理,处理后的结果依然是Stream对象。

--第三步: 对Stream对象的最后整合处理。处理后的结果一般情况下都不再是Stream对象,可能是一个具体的数字,字符串,或者一个新的集合。

-- 注意:整个过程中,数据源本身并不会发生变化。

2.4 数据源的获取

2.4.1 数据源的介绍

数据源,顾名思义,既是流中的数据的来源。是集合的流式编程的第一步,将数据源中的数据读取到流中,进行处理。

注意:将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序... ,此时是不会影响数据源中的数据。

2.4.2 数据源的获取

这个过程,其实是将一个容器中的数据,读取到一个流中。因此无论什么容器作为数据源,读取到流中 的方法返回值一定是一个Stream。

1)stream():获取的流对象,是串行的,不是并行的,好比只有一个人工作。

2)parallelStream():获取的流对象,是并行的,好比有好多个人一起工作,效率高。

java 复制代码
public static void main(String[] args) {
    List<Integer> nums = new ArrayList<>();
    nums.add(1);
    nums.add(2);
    nums.add(3);
    nums.add(4);
    nums.add(5);
    /**
     * 如果想要对这个集合进行流式编程,第一步,必须获取数据源(关联数据源)
     */
    //1. 获取的流对象,是串行的,不是并行的,好比只有一个人工作。
    Stream<Integer> stream = nums.stream();
    //2. 获取的流对象,是并行的,好比有好多个人一起工作,效率高。
    Stream<Integer> stream1 = nums.parallelStream();
}

2.5 最终操作

2.5.1 最终操作的简介

将流中的数据整合到一起。可以存入一个新的集合,也可以直接对流中的数据遍历,或者统计。

注意事项:

最终操作,会关闭这个流。流里的数据都会被销毁。如果在关闭流的基础上,继续操作流,会报如下异常:

stream has already been operated upon or closed

2.5.2 最终操作的常用方法

1)collect

  1. 收集方法,可以将流的数据搜集成一个新的集合。

  2. 该方法的形参是一个Collector接口(非函数式接口),可以用来指定收集规则。

  3. 通常情况下,不需要程序员自己实现,Collectors工具类里提供的方法够用。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
//获取数据源
Stream<Integer> stream = nums.stream();
//搜集1:搜集成List集合
List<Integer> c1 = stream.collect(Collectors.toList());
System.out.println(c1 == nums);
//搜集2:搜集成Set集合
//Set<Integer> c2 = stream.collect(Collectors.toSet());
//System.out.println(c2);

搜集成Map集合:

Collectors.toMap(KeyMapper,ValueMapper)

KeyMapper是一个函数式接口,里面有一个R apply(T t)抽象方法,我们就是通过lambda表达式 来重写apply方法。 ValueMapper亦是如此。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
//获取数据源
Stream<Integer> stream = nums.stream();
Map<String,Integer> c3 = stream.collect(Collectors.toMap((e)->"Key"+e, e->e));
System.out.println(c3);
运行结果:
{Key2=2, Key1=1, Key5=5, Key4=4, Key3=3}

2)reduce

将流中的数据按照一定的规则聚合起来。

返回的类型:Optional,需要调用它的get方法,获取里面的数据。

从下面的案例可以得出结论: a变量接收的是数据源中的第一个元素,然后b变量接收的是剩下的元素。 相当于: a-=b

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
Stream<Integer> stream = nums.stream();
Optional<Integer> reduce = stream.reduce((a, b) -> a - b); 
int result = reduce.get();
System.out.println("计算结果:"+result);//-13

3)count

用于统计数据源中的元素数量。

底层源码: return mapToLong(e->1L).sum();

即将元素映射成1,然后求和。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
Stream<Integer> stream = nums.stream();
long count = stream.count();
System.out.println("count = " + count);//count = 5

4)forEach

对流中的数据进行遍历,注意遍历完毕,流就关闭了。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
Stream<Integer> stream = nums.stream();
stream.forEach(num -> System.out.println(num));
stream.forEach(System.out::print);

5)max & min

获取流中的最大的元素、最小的元素。

  1. max(Comparator c):获取排序后的最后一个元素。
java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
Stream<Integer> stream = nums.stream();
int m = stream.max((a,b) -> a - b).get();
System.out.println(m);
  1. min(Comparator c):获取排序后的第一个元素。
java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
Stream<Integer> stream = nums.stream();
int n = stream.min((a,b) -> a - b).get();
System.out.println(n);

6)Matching

  1. allMatch: 当数据源中的所有元素都满足匹配条件,才返回true。
java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
boolean b = nums.stream().allMatch(e -> e < 10);
System.out.println(b);
运行结果:true
  1. anyMatch: 当数据源中的任意一个元素满足匹配条件,就返回true。
java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
boolean b = nums.stream().anyMatch(e -> e < 3);
System.out.println(b);
运行结果:true
  1. noneMatch: 当数据源中的所有元素都不满足匹配条件,才返回true。
java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
boolean b = nums.stream().noneMatch(e -> e < 0);
System.out.println(b);
运行结果:true

7)find

  1. findFirst:从流中获取一个元素(一般情况下,是获取的开头的元素)

  2. findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)

注意:上述两个方法,针对于串行(同步)的流,获取的都是第一个元素。 针对于并行(异步)的流,获取的应该不同,但也可以相同。

java 复制代码
public static void main(String[] args) {
    List<Integer> nums = new ArrayList<>();
    nums.add(1);
    nums.add(2);
    nums.add(3);
    nums.add(4);
    nums.add(5);
    //串行的流演示
    int e1 = nums.stream().findFirst().get();
    System.out.println(e1);//1
    int e2 = nums.stream().findAny().get();
    System.out.println(e2);//1
    //并行的流演示
    int e3 = nums.parallelStream().findFirst().get();
    System.out.println("e3: "+e3);
    int e4 = nums.parallelStream().findAny().get();
    System.out.println("e4: "+e4);
}

2.6 中间操作

2.6.1 filter

过滤出来满足条件的数据,比如想过滤出集合中的所有奇数。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
nums.add(2);
nums.add(4);
//比如想要所有的偶数
List<Integer> c1 = nums.stream().filter(x -> x % 2 != 0).collect(Collectors.toList());
System.out.println(c1);
运行结果:[1, 3, 5]

2.6.2 distinct

去重,去掉集合里重复的数据。

java 复制代码
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
nums.add(2);
nums.add(4);
nums.stream().distinct().forEach(System.out::println);
运行结果:
1
2
3
4
5

2.6.3 sorted

  1. sorted():升序排序。

  2. sorted(Comparator c): 自定义比较规则。

java 复制代码
List<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
nums.add(1);
nums.stream().sorted().forEach(System.out::println);
System.out.println("----------------------------------");
nums.stream().sorted((a,b) -> b - a).forEach(System.out::println);
运行结果:
1
2
3
4
5
----------------------------------
5
4
3
2
1

2.6.4 limit

limit(long size): 表示截取流中的前size个元素。

java 复制代码
List<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
nums.add(1);
nums.stream().limit(2).forEach(System.out::println);
运行结果:
3
2

2.6.5 skip

skip(long size): 表示跳过前size个元素。

java 复制代码
List<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
nums.add(1);
nums.stream().skip(2).forEach(System.out::println);
运行结果:
5
4
1

2.6.6 map

map(.....): 将元素映射成另外一种类型。

比如:将元素映射成字符串类型。

java 复制代码
List<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
nums.add(1);
List<String> c1 = nums.stream().map(e -> ""+e).collect(Collectors.toList());

2.6.7 mapToXXX

mapToInt(....):将元素映射成intStream

mapToLong(....):将元素映射成longStream

mapToDouble(....):将元素映射成doubleStream

下面就是将所有元素映射成1。

java 复制代码
nums.stream().mapToInt(e->1)

2.6.8 flatMap

扁平式映射,一般针对的都是集合元素仍然是一个集合。

普通的集合:[1,2,3,4,5]

集合元素是集合的:[[1,2],[1,3,4],[2,4,5]]

扁平式映射:就是将元素是集合的这种特殊集合,转成普通的集合。

如将集合:[[1,2,10],[1,3,4],[2,4,5]] 转成该形式: [1,2,10,1,3,4,2,4,5]

flatMap(.....) 传入一个lambda表达式 :e->e.stream() 表示压平了。

java 复制代码
List<List<Integer>> out = new ArrayList<>();
out.add(Arrays.asList(1,2,3));
out.add(Arrays.asList(4,5,6));
out.add(Arrays.asList(3,4,5));
long count = out.stream().flatMap(e->e.stream()).count();
System.out.println(count);
double asDouble = out.stream().flatMap(e->e.stream())
    .mapToInt(e->e).average().getAsDouble();
System.out.println(asDouble);
int a = out.stream().flatMap(e->e.stream()).mapToInt(e->e).max().getAsInt();
System.out.println(a);
运行结果:
9
3.6666666666666665
6

2.7 Collectors工具类

Collectors是一个工具类,里面封装了很多方法,可以很方便的获取到一个 Collector 接口的实现类对 象,从而可以使用 collect() 方法,对流中的数据,进行各种各样的处理、整合。

java 复制代码
Collectors.toList() : 将流中的数据,聚合到一个  List 集合中 
Collectors.toSet()  : 将流中的数据,聚合到一个  Set 集合中   
Collectors.toMap()  : 将流中的数据,聚合到一个  Map 集合中
maxBy()             : 按照指定的规则,找到流中最大的元素,等同于  max
minBy()             : 按照指定的规则,找到流中最小的元素,等同于  min

joining()           : 将流中的数据拼接成一个字符串,注意:只能操作流中是String的数据

summingInt()        : 将流中的数据,映射成  int 类型的数据,并求和
averagingInt()      : 将流中的数据,映射成  int 类型的数据,并求平均值
summarizingInt()    : 将流中的数据,映射成  int 类型的数据,并获取描述信息
java 复制代码
// maxBy: 按照指定的规则,找到流中最大的元素,等同于  max
Student max = list.stream()
   .collect(Collectors.maxBy((s1, s2) -> s1.getScore() -s2.getScore()))
   .get();
System.out.println(max);

// minBy: 按照指定的规则,找到流中最小的元素,等同于  min
Student min = list.stream()
.collect(Collectors.minBy((s1, s2) -> s1.getScore() -s2.getScore()))
.get();
System.out.println(min);


// 将流中的数据,拼接起来
String s1 = list.stream()
   .map(Student::getName)
   .collect(Collectors.joining());
System.out.println(s1);

// 将流中的数据,拼接起来,以指定的分隔符进行分隔
String s2 = list.stream()
   .map(Student::getName)
   .collect(Collectors.joining(", "));
System.out.println(s2);

// 将流中的数据,拼接起来,以指定的分隔符进行分隔,并添加前缀和尾缀
//第一个参数表示以指定的分隔符进行分隔,第二个表示前缀,第三个表示后缀。
String s3 = list.stream()
   .map(Student::getName)
   .collect(Collectors.joining(", ", "{", "}"));
System.out.println(s3);

// 将流中的数据,映射成  int 类型的数据,并求和
int sum = list.stream()
   .collect(Collectors.summingInt(Student::getScore));
System.out.println(sum);

// 将流中的数据,映射成  int 类型的数据,并求平均值
double average = list.stream()
   .collect(Collectors.averagingInt(Student::getScore));
System.out.println(average);

// 将流中的数据,映射成 int 类型的数据,并获取描述信息
IntSummaryStatistics summaryStatistics = list.stream()
   .collect(Collectors.summarizingInt(Student::getScore));

System.out.println(summaryStatistics);
System.out.println(summaryStatistics.getCount());
System.out.println(summaryStatistics.getSum());
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getMin());
System.out.println(summaryStatistics.getAverage());
相关推荐
zjw_rp5 分钟前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob18 分钟前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder26 分钟前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
engchina34 分钟前
如何在 Python 中忽略烦人的警告?
开发语言·人工智能·python
向宇it35 分钟前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行37 分钟前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
诚丞成1 小时前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
Smile灬凉城6661 小时前
反序列化为啥可以利用加号绕过php正则匹配
开发语言·php
lsx2024061 小时前
SQL MID()
开发语言
Dream_Snowar1 小时前
速通Python 第四节——函数
开发语言·python·算法