欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8新特性之Lambda表达式 使用详解

程序员文章站 2022-07-10 17:43:15
Lambda语法介绍、集合操作、排序、分组、收集等...

Java8新特性

前面章节介绍基础语法,后面章节介绍类库,可查看目录选择

1、lambda表达式简介

  • 什么是lambda表达式

    • lambda表达式是JAVA 8 添加的一个新特性,也是一个匿名函数
  • 为什么要使用lambda表达式

    • 使用lambda表达式可以对一个接口进行非常简洁的实现
    public class LambdaTest01 {
    
        public static void main(String[] args) {
            // 1、使用接口实现
            MyRunnable myRunnable = new MyRunnable();
            myRunnable.run();
            //2、使用匿名内部类
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("匿名内部类");
                }
            };
            runnable.run();
            // 3、使用lambda表达式
            Runnable runnable1 = () -> System.out.println("Lambda表达式");
            runnable1.run();
            
        }
    
    }
    class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            System.out.println("接口实现");
        }
    }
    
    • lambda表达式是延迟执行的,满足了条件 lambda体才会执行
    public class LambdaTest02 {
        public static void main(String[] args) {
            test02(2,()->
                {
                    System.out.println("执行了");
                    return true;
                });
        }
    
        public static void test02(Integer num, Test test) {
            if (num == 1) {
                System.out.println(test.mains());
            }
        }
    }
    
    @FunctionalInterface
    public interface Test {
        boolean mains();
    }
    
  • lambda表达式对接口的要求:必须是函数式接口

    • 接口中有且只有一个抽象方法。
    • 通过注解@FunctionalInterface修饰
    @FunctionalInterface
    public class MyFunctionalInterface {
      	void hello();
    }
    

2、Lambda表达式基础语法

  • Java8中引入了新的操作符"->" ,读作goes to(lambda操作符)
    • 左侧 (): lambda表达式的参数列表
    • 右侧 {} :lambda表达式所需执行的功能(lambda体)
  • 可以对接口的抽象方法进行实现
  • lambda表达式需要"函数式接口"的支持
  • 函数式接口:有且只有一个抽象方法的接口,称为函数式接口,可以通过注解@FunctionalInterface修饰
  • 语法格式
//1、无参数,无返回值
Runnable r2 = () -> {System.out.println("hello lambda")};
//2、无参数,有返回值
Supplier<Integer> supplier = () -> {return 1;};
//3、一个参数,无返回值
Consumer<Integer> consumer = (Integer x) -> {System.out.println(x)};
consumer.accept("hello world");
//4、一个参数,有返回值
Function<Integer, Integer> function = (Integer x) -> {
            return  x + 1;
        };
//5、多个参数,无返回值
BiConsumer<Integer,Integer> consumer = (Integer x, Integer y) -> {
            System.out.println(x + y);
};
//6、多个参数,有返回值
 Comparator<Integer> com = (Integer x, Integer y) -> {
            return Integer.compare(1, 2);
 };

##3、Lambda表达式语法简化

/*
	1.参数类型
		- 由于在接口的抽象方法中定义了参数的数量和类型,所以在lambda表达式中,参数的类型可以省略
		- 注意:要省略所有参数的都需要省略
*/
	Consumer<Integer> consumer = (x) -> {System.out.println(x);};
/*
	2.参数的小括号
		- 如果参数列表的参数只有一个,则小括号可以省略
*/
	Consumer<Integer> consumer = x -> {System.out.println(x);};
/*
	3.方法体的大括号
		- 如果方法体中只有一条语句,则大括号可以省略
*/
	Consumer<Integer> consumer = x -> System.out.println(x);
/*
	4.方法的return
		- 如果方法体中只有一条return语句,则大括号和return都可以省略
*/
	 Function<Integer, Integer> function =  x -> x + 1;

注意:如果lambda体中引用了外部变量,则外部变量无法进行修改

int a = 10;
//此处编译报错:a必须被final修饰
Consumer consumer1 = e -> System.out.println(a);
a++;
consumer1.accept(10);

4、Java8内置的四大核心函数式接口

接口名称 接口类型 方法
Consumer 消费型接口 void accept(T t);
Supplier 供给型接口 T get();
Function<T,R> 函数型接口 R apply(T t);
Predicate 断言型接口 boolean test(T t);

4.1 接口中抽象方法的引用

public class TestLambda {

    public static void main(String[] args) {
      
 				//Consumer<T>:消费型接口
        happy(2000,(x)-> System.out.println("买东西消费"+x+"元"));


        Random random = new Random();
				//Supplier<T>:供给型接口
        List<Integer> num = getNum(10, () -> random.nextInt(100));
        for (Integer integer : num) {
            System.out.println(integer);
        }
      
      
				//Function<T,R>:函数型接口
        String s = strHandler("\t\t\t hello world", (str) -> str.trim());
        System.out.println(s);
				
        String s1 = strHandler(s, (str) -> str.substring(0, 5));
        System.out.println(s1);

        List<String> strings = Arrays.asList("hello", "world", "www", "aa");
       //Predicate<T> : 断言型接口
        List<String> list = getList(strings, (str) -> str.length() <= 3);
        for (String s2 : list) {
            System.out.println(s2);
        }
    }

    public static void happy(double money,Consumer<Double> con) {
        con.accept(money);
    }
    
    //产生指定个数的整数,并放入到集合中去
    public static List<Integer> getNum(int num, Supplier<Integer> supplier) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer ints = supplier.get();
            list.add(ints);
        }
        return list;
    }

    public static String strHandler(String str, Function<String, String> function) {
        return function.apply(str);
    }

    public static List<String> getList(List<String> list, Predicate<String> predicate) {
        List<String> results = new ArrayList<>();
        for (String s : list) {
            if (predicate.test(s)) {
                results.add(s);
            }
        }
        return results;
    }
}
  • 其他函数式接口
函数式接口 参数类型 返回类型 用途
BiFunction<T,U,R> T,U R 对类型为T,U参数应用操作,返回R类型结果。包含方法R apply(T t,U u);
UnaryOperator(Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法T apply(T t);
BInaryOperator (BiFunction子接口) T,T T 对类型为T的对象进行二元运算,并返回T类型的结果。T apply(T t1,T t2);
BiConsumer<T,U> T,U void 对类型为T,U参数应用操作,包含方法void accept(T t,U u);
ToIntFunction、ToLongFunction、ToDoubleFunction T Int,long,double 分别计算int、long、double值的函数
IntFunction、LongFunction、DoubleFunction Int,long,double R 参数分别为int、long、double类型的函数

注意:Java8对四大核心函数式接口的进行了很多拓展,详情请查看API

4.2 接口中默认方法的引用

接口名称 方法名称 抽象/默认 延迟/终结
Supplier get 抽象 终结
Consumer accept 抽象 终结
andThen 默认 延迟
Predicate test 抽象 终结
and 默认 延迟
or 默认 延迟
negate 默认 延迟
Function apply 抽象 终结
andThen 默认 延迟
  • 延迟方法:只是在拼接Lambda函数模型的方法,并不立即执行得到结果。
  • 终结方法:根据拼好的Lambda函数模型,立即执行得到结果值的方法。

通常情况下,这些常用的函数式接口中唯一的抽象方法为终结方法,而默认方法为延迟方法。

Predicate<Integer> predicate1 = p -> p > 20;
boolean test1 = predicate1.or(p -> p < 10).test(12);
System.out.println(test1);

Predicate<Integer> predicate2 = p -> p < 20;
boolean test2 = predicate2.and(p -> p >10).test(30);
System.out.println(test2);

备注:JDK中更多内置的常用函数式接口,请参考 java.util.function 包的API文档

5、引用

1)、方法引用

  • 若lambda体中的内容有方法已经实现了,我们可以使用"方法引用"(可以理解为方法引用是lambda表达式的另一种体现形式)

  • 语法格式 (方法的隶属者::方法名)

    方法的隶属者:即被调用方法隶属于谁,比如静态方法隶属于类,实例方法隶属于类或者对象

    1、对象::实例方法(没有用static修饰,也叫非静态方法)
            Consumer<String> consumer = (x) -> System.out.println(x);
            consumer.accept("hello world");
    等价于:    
            Consumer<String> consumer1 = System.out::println;
            consumer1.accept("a");
    2、类::类方法(用static修饰,也叫静态方法)
    				Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
            int compare1 = comparator.compare(1, 2);
            System.out.println(compare1);
    等价于:
            Comparator<Integer> comparator1 = Integer::compare;
            int compare = comparator.compare(2, 1);
            System.out.println(compare);
    3、类::实例方法名
    				BiPredicate<String, String> (x,y)->x.equals(y);
            boolean test = bp.test("哈哈", "哈哈");
            System.out.println(test);
    等价于:
            BiPredicate<String, String> bp = String::equals;
            boolean test = bp.test("哈哈", "哈哈");
            System.out.println(test);
    

    注意:参数数量和类型、返回值的类型,一定要和接口中所定义方法的参数和返回值一致

2)、构造器引用

  • 格式:ClassName::new

    1、无参构造器(调用无参构造)
    		Supplier<Employee> sup = () -> new Employee();
    等价于:
     		Supplier<Employee> sup1 = Employee::new;
    2、有参构造器(根据函数式接口传入的参数,选择调用的构造方法)
     		Function<Integer,Employee> fun = (x) -> new Employee(x);
     		Employee apply = fun.apply(2);
     		System.out.println(apply);
    等价于:
     		Function<Integer,Employee> fun = Employee::new;
     		Employee employee1 = fun.apply(1);
     		System.out.println(employee1);
    

    注意:需要调用的构造器的参数列表与函数式接口中抽象方法的参数列表保持一致,根据参数个数选择构造方法

3)、数组引用

  • 格式: Type::new

        Function<Integer, String[]> function1 = (x) -> new String[x];
        String[] apply1 = function1.apply(5);
        System.out.println(apply1.length);
    等价于:
        Function<Integer, String[]> function = String[]::new;
        String[] strings = function.apply(10);
        System.out.println(strings.length);
    

    注意:数组的初始化大小和传入的参数相关

6、集合操作

  • 迭代操作
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
list.forEach(System.out::println);

list.forEach(x->{
  	if (x%2 == 0) {
       System.out.println(x);
    }
});
  • 删除操作
List<Employee> employees = new ArrayList<>();
        Collections.addAll(employees, new Employee("张三", 12, 1),
                new Employee("李四", 22, 2),
                new Employee("田七", 12, 2),
                new Employee("王五", 24, 3),
                new Employee("赵六", 34, 4));
        employees.removeIf(e -> e.getAge() > 20);
        System.out.println(employees);

7、Stream(核心流)

  • 什么是stream
    • stream 是数据渠道(集合元素的函数模型),用于操作数据源(集合、数组等)所生成的元素序列,可以认为它是一个高级版本的 Iterator。
    • “集合讲的是数据,流讲的是计算”
  • 获取stream流的方式
    • java.util.Collection 接口中加入了默认stream()方法,所以其所有实现类均可获取流。
    • Stream.of(T t)
  • 注意:
  • 1⃣️ stream 自己不会存储数据元素,而是通过管道将数据源的元素传递给操作。
  • 2⃣️ stream不会改变源对象。相反,他们会返回一个持有结果的新stream
  • 3⃣️ stream操作是有延迟执行的,Stream的操作由零个或多个中间操作和一个终止操作两部分组成。只有执行了终止操作,Stream定义的中间操作才会依次执行。
import com.sun.org.apache.bcel.internal.generic.NEW;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * 一、Stream的三个操作步骤:
 *  1、创建Stream
 *  2、中间操作
 *  3、终止操作(终端操作)
 */
public class TestLambda3 {

    //创建流
    public static void main(String[] args) {
        //1、通过Collection系列提供的stream()或parallelStream()
        List<Object> list = new ArrayList<>();
        Stream<Object> stream1 = list.stream();

        //2、通过Arrays中的静态方法stream()获取数组流
        Employee[] employees = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(employees);

        //3、通过stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

        //4、创建无限流
        //方式一 迭代
        Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
        stream4.limit(10).forEach(System.out::println);

        //方式二 生成
        Stream.generate(() -> Math.random()).forEach(System.out::println);
    }
}

8、筛选和切片(中间操作)

  • filter — 接收lambda,从流中排除某些元素
  • limit — 截断流,使其元素不超过给定数量
  • skip(n) — 跳过元素,跳过前n个元素,取后面的元素,若流中元素不足n个,则返回一个空流,与limit互补
  • distinct — 筛选,通过流产生元素的hashCode() 和equals() 去除重复元素
public class TestLambda4 {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(new Employee("张三", 12, 1),
                new Employee("李四", 22, 2),
                new Employee("王五", 24, 3),
                new Employee("李四", 34, 4),
                new Employee("李四", 34, 4)
        );

        employees.stream().filter(x -> x.getAge() > 20).forEach(System.out::println);

        System.out.println("=========================================");

        employees.stream().filter(x -> x.getAge()>20).limit(2).forEach(System.out::println);

        System.out.println("=========================================");

        employees.stream().filter(x->x.getAge()>20).skip(2).forEach(System.out::println);

        System.out.println("=========================================");
        
      	//需重写hashcode和equals方法
        employees.stream().distinct().forEach(System.out::println);
    }
}

运行结果:

Employee{name='李四', age=22, id=2}
Employee{name='王五', age=24, id=3}
Employee{name='李四', age=34, id=4}
Employee{name='李四', age=34, id=4}
=========================================
Employee{name='李四', age=22, id=2}
Employee{name='王五', age=24, id=3}
=========================================
Employee{name='李四', age=34, id=4}
Employee{name='李四', age=34, id=4}
=========================================
Employee{name='张三', age=12, id=1}
Employee{name='李四', age=22, id=2}
Employee{name='王五', age=24, id=3}
Employee{name='李四', age=34, id=4}

对象去除重复的方式

List<Person> data = list
                                .stream().collect(Collectors.collectingAndThen(Collectors.toCollection(
                                        () -> new TreeSet<>(Comparator.comparing(Person::getId))), ArrayList::new));

9、映射(中间操作)

  • map — 接收lambda,将元素转换为其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,将其映射成一个新的元素
  • flatMap — 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
//map()

public static void main(String[] args) {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
        list.stream().map((x) -> x.toUpperCase()).forEach(System.out::println);

        List<Employee> employees = Arrays.asList(new Employee("张三", 12, 1),
                new Employee("李四", 22, 2),
                new Employee("王五", 24, 3),
                new Employee("李四", 34, 4),
                new Employee("李四", 34, 4)
        );

        employees.stream().map(Employee::getName).forEach(System.out::println);
    }
结果:
AAA
BBB
CCC
DDD
张三
李四
王五
李四
李四
//flatMap()

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class TestLambda5 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
      //类似于 {{a,a,a},{b,b,b}....}
      Stream<Stream<Character>> stream = list
        .stream()
        .map(TestLambda5::filterCharacter);
      stream.forEach((x) -> x.forEach(System.out::println));
      //将多个流合并成一个流
      //类似于 {a,a,a,b,b,b....}
      list
        .stream()
        .flatMap(TestLambda5::filterCharacter)
        .forEach(System.out::println);
    }
  
  
    public static Stream<Character> filterCharacter(String string) {
        List<Character> list = new ArrayList<>();

        for (Character c : string.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
}

10、排序(中间操作)

10.1 基本数据类型及其包装类排序

  • sorted() — 自然排序(Comparable)
  • sorted(Comparator com) — 定制排序
import java.util.Arrays;
import java.util.List;
/**
 * 自然排序
 */
public class TestLambda6 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("ddd","aaa", "bbb", "ccc");
        
        list.stream().sorted().forEach(System.out::println);
    }
}
结果:
aaa
bbb
ccc
ddd

10.2 引用数据类型排序

import java.util.Arrays;
import java.util.List;

/**
* 定制排序
*/
public class TestLambda6 {
  public static void main(String[] args) {

      List<Employee> employees = Arrays.asList(new Employee("张三", 12, 1),
              new Employee("李四", 22, 2),
              new Employee("田七", 12, 2),
              new Employee("王五", 24, 3),
              new Employee("赵六", 34, 4)
      );

      employees.stream().sorted((e1,e2)->{
          if ((e1.getAge()).equals(e2.getAge())) {
              return (e1.getName()).compareTo((e1.getName()));
          }else{
              return (e1.getAge()).compareTo(e2.getAge());
          }
      }).forEach(System.out::println);
  }
}
结果:
Employee{name='张三', age=12, id=1}
Employee{name='田七', age=12, id=2}
Employee{name='李四', age=22, id=2}
Employee{name='王五', age=24, id=3}
Employee{name='赵六', age=34, id=4}

10.3 TreeSet排序

TreeSet<Integer> set = new TreeSet();
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(4);
        System.out.println(set);
//结果:
//[1, 2, 3, 4]

TreeSet<Employee> treeSet = new TreeSet<Employee>((x, y) -> {
            if (x.getAge()<=y.getAge()) {
                return -1;
            }else{
                return 1;
            }
        });

        treeSet.add(new Employee("李四", 22, 2));
        treeSet.add(new Employee("王五", 24, 3));
        treeSet.add(new Employee("李四", 44, 4));
        treeSet.add(new Employee("李四", 34, 4));
        System.out.println(treeSet);
//结果:
/*
[Employee{name='李四', age=22, id=2, status=null},
Employee{name='王五', age=24, id=3, status=null}, 
Employee{name='李四', age=34, id=4, status=null}, 
Employee{name='李四', age=44, id=4, status=null}]
*/

注意:如果TreeSet传递的函数式接口:如果return 0;则俩者值相同,会进行去重复操作

11、查找与匹配(终止操作)

方法名称 方法作用
allMatch 检查是否匹配所有元素
anyMatch 检查是否至少匹配一个元素
noneMatch 检查是否没有匹配所有元素
findFirst 返回第一个元素
findAny 返回当前流中的任意元素
count 返回流中元素的总个数
max 返回流中最大值
min 返回流中最小值
import java.util.Arrays;
import java.util.List;

/**
 * 查找 匹配
 */
public class TestLambda7 {
    public static void main(String[] args) {
        List<Employee> employees = Arrays
          .asList(new Employee("张三", 12, 1, Employee.Status.BUSY),
                new Employee("李四", 22, 2, Employee.Status.FREE),
                new Employee("田七", 12, 2, Employee.Status.BUSY),
                new Employee("王五", 24, 3, Employee.Status.FREE),
                new Employee("赵六", 34, 4, Employee.Status.VOCATION)
        );
				//false 检查是否匹配所有元素
        boolean b = employees.stream()
          .allMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b);
				//true 检查是否至少匹配一个元素
        boolean b1 = employees.stream()
          .anyMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1);
				//false 检查是否没有匹配所有元素
        boolean b2 = employees.stream()
          .noneMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2);
        //Employee{name='赵六', age=34, id=4, status=VOCATION} 
      	//返回第一个元素
				Optional<Employee> optional = employees.stream()
          .filter(x -> x.getAge() > 25)
          .findFirst();
        System.out.println(optional.get());
        //返回当前流中的任意元素
      	Optional<Employee> optional = employees.stream().findAny();
        System.out.println(optional.get());
        //2  返回流中元素的总个数
        long count = employees.stream().
          filter(x -> x.getStatus().equals(Employee.Status.BUSY))
          .count();
        System.out.println(count);
				//12 返回流中最小值
        Optional<Employee> min = employees.stream()
          .min((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
        System.out.println(min.get().getAge());

				//34 返回流中最大值
        Optional<Integer> min = employees.stream()
          .map(Employee::getAge)
          .min(Integer::compare);
        System.out.println(min.get());
    }
}

12、归约与收集(终止操作)

方法名称 方法作用
reduce(T identity,BinaryOperator) /reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值
collect 将流转换成其他形式,接收一个Collector接口的实现

12.1、reduce收集

//归约
public class TestLambda8 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        List<Employee> employees = Arrays.asList(new Employee("张三", 12, 1, Employee.Status.BUSY),
                new Employee("李四", 22, 2, Employee.Status.FREE),
                new Employee("田七", 12, 2, Employee.Status.BUSY),
                new Employee("王五", 24, 3, Employee.Status.FREE),
                new Employee("赵六", 34, 4, Employee.Status.VOCATION)
        );
        Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce);

        Optional<Integer> optional = employees.stream().map(Employee::getAge).reduce(Integer::sum);
        System.out.println(optional.get());
    }
}

12.2、常见集合set、list、map收集

/**
 * 收集
 */
public class TestLambda8 {
    public static void main(String[] args) {
        List<Employee> employees = Arrays
          .asList(new Employee("张三", 12, 1, Employee.Status.BUSY),
                new Employee("李四", 22, 2, Employee.Status.FREE),
                new Employee("田七", 12, 2, Employee.Status.BUSY),
                new Employee("王五", 24, 3, Employee.Status.FREE),
                new Employee("赵六", 34, 4, Employee.Status.VOCATION)
        );
				//收集成list集合
        List<String> collect = employees.stream()
          .map(Employee::getName).collect(Collectors.toList());
        System.out.println(collect); //[张三, 李四, 田七, 王五, 赵六, 赵六]
      
				//收集成set集合
        Set<String> set = employees.stream()
          .map(Employee::getName).collect(Collectors.toSet());
        System.out.println(set);//[李四, 张三, 王五, 赵六, 田七]

      	//收集成指定的类型
				HashSet<String> hashSet = employees.stream()
          .map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);//[李四, 张三, 王五, 赵六, 田七]
    }
  			//总数
  		  Long aLong = employees.stream().collect(Collectors.counting());
        System.out.println(aLong); //6
  			
  			//平均值
  			Double aDouble = employees.stream().collect(Collectors.averagingInt(Employee::getAge));
        System.out.println(aDouble);
  			//总和
  			Integer collect2 = employees.stream()
          .collect(Collectors.summingInt(Employee::getAge));
        System.out.println(collect2); //138
  
  			//最大值
  			Optional<Employee> collect1 = employees
          .stream()
          .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())));
  			Employee{name='赵六', age=34, id=4, status=VOCATION}
        System.out.println(collect1.get()); 
  
  			//最小值
        Optional<Integer> optional1 = employees.stream()
          .map(Employee::getAge)
          .collect(Collectors.minBy(Integer::compare));
        System.out.println(optional1.get()); //12
  
  			//其他方法
  			IntSummaryStatistics iss = employees.stream()
          .collect(Collectors.summarizingInt(Employee::getAge));
        System.out.println(iss.getMax());
        System.out.println(iss.getAverage());
        System.out.println(iss.getCount());
        System.out.println(iss.getMax());
        System.out.println(iss.getMin());

				//连接
				String collect4 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(","));
  			//张三,李四,田七,王五,赵六,赵六
        System.out.println(collect4);
				String collect4 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",""===""==="));
  		  //===张三,李四,田七,王五,赵六,赵六===
        System.out.println(collect4);
}

12.3、分组及分区

groupingBy(classifier, HashMap::new, downstream);
// 参数一:Function类型函数式接口,指定分组方式
// 参数二:指定返回值类型
// 参数三:收集器

12.3.1、无序分组

List<Employee> employees = Arrays
          .asList(new Employee("张三", 12, 1, Employee.Status.BUSY),
                new Employee("李四", 22, 2, Employee.Status.FREE),
                new Employee("田七", 12, 2, Employee.Status.BUSY),
                new Employee("王五", 24, 3, Employee.Status.FREE),
                new Employee("赵六", 34, 4, Employee.Status.VOCATION)
        );
System.out.println(map);
  //分组
Map<Employee.Status, List<Employee>> map = employees.stream()
		.collect(Collectors.groupingBy(Employee::getStatus));
/*
	{FREE=[
         Employee{name='李四', age=22, id=2, status=FREE}, 
         Employee{name='王五', age=24, id=3, status=FREE}], 
	VOCATION=[
         Employee{name='赵六', age=34, id=4, status=VOCATION}, 
         Employee{name='赵六', age=34, id=4, status=VOCATION}], 
	BUSY=[
         Employee{name='张三', age=12, id=1, status=BUSY}, 
         Employee{name='田七', age=12, id=2, status=BUSY}
         ]}
*/

13.3.2、有序分组

//有序分组,指定收集类型为LinkedHashMap
LinkedHashMap<Employee.Status, List<Employee>> map = employees.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, LinkedHashMap::new, Collectors.toList());	

13.3.2、多级分组

  • 方式一 groupingBy多层嵌套
//多级分组 
Map<Employee.Status, Map<String, List<Employee>>> collect3
     = employees.stream()
     .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(x -> {
       if (x.getAge() < 30) {
           return "青年";
       } else if (x.getAge() < 60) {
           return "中年";
       } else {
           return "老年";
       }
   })));
/*
{FREE={
     青年=[
       Employee{name='李四', age=22, id=2, status=FREE}, 
       Employee{name='王五', age=24, id=3, status=FREE}]}, 
VOCATION={
     中年=[
       Employee{name='赵六', age=34, id=4, status=VOCATION}, 
       Employee{name='赵六', age=34, id=4, status=VOCATION}]}, 
BUSY={
     青年=[
     	Employee{name='张三', age=12, id=1, status=BUSY}, 
       Employee{name='田七', age=12, id=2, status=BUSY}
       ]}}
*/
   System.out.println(collect3);
  • 方式二 Pair.of(T t1, T t2)
// 根据proId和processId联合主键分组
 LinkedHashMap<Pair<String, String>, List<YlProcessInfoTask>> map = processInfos
                    .stream()
                    .collect(Collectors.groupingBy(v -> Pair.of(v.getProId(), v.getProcessId()), LinkedHashMap::new, Collectors.toList()));

13.3.3、分区

//分区
Map<Boolean, List<Employee>> map1 = employees.stream()
     .collect(Collectors.partitioningBy(x -> x.getAge() > 30));
System.out.println(map1);
/*
	{false=[
         Employee{name='张三', age=12, id=1, status=BUSY}, 
         Employee{name='李四', age=22, id=2, status=FREE}, 
         Employee{name='田七', age=12, id=2, status=BUSY}, 
         Employee{name='王五', age=24, id=3, status=FREE}
         ], 
	true=[
         Employee{name='赵六', age=34, id=4, status=VOCATION}, 
         Employee{name='赵六', age=34, id=4, status=VOCATION}
        ]
	}
*/

13、并行流和串行流

  • 了解fork/join框架:就是在必要的时候,将一个大任务,进行拆分(fork)成若干个小任务(拆分法哦不可再拆分),在将一个个小的任务的运算结果进行join汇总

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzJAQnjl-1605781770016)(/Users/mac/Documents/fork:join.png)]

  • fork/join :采用"工作窃取模式" :当执行新的任务时,它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后在从一个随机线程的队列中偷一个并将它放在自己的队列中。
    • Java8 中 串行和并行的切换:parallel()和sequential()进行切换
//切换成并行流
LongStream
	.rangeClosed(0, 1000000000l)
  .parallel()
  .reduce(0, Long::sum);
//切换成串行流
LongStream
	.rangeClosed(0, 1000000000l)
  .sequential()
  .reduce(0, Long::sum);

本文地址:https://blog.csdn.net/kehonghg/article/details/109820986