• Java 8新特性


    Java 8新特性

    一、并行流和串行流

    • 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数 据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
    • Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过parallel()
      与sequential() 在并行流与顺序流之间进行切换。

    二、Lambda表达式

    Lambda 是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

    • 从匿名类到Lambda 的转换
     //匿名内部类
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Lambda");
            }};
            //Lambda
            Runnable runnable1 = ()-> System.out.println("Lambda");
    
           //使用匿名内部类作为参数传递
            TreeSet<String> list = new TreeSet<String>(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return Integer.compare(o1.length(), o2.length());
                }
            });
            //lambda作为参数传递
            TreeSet<String> set = new TreeSet<>(
                    ((o1, o2) -> Integer.compare(o1.length(), o2.length()))
            );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为“->” ,该操作符被称为Lambda 操作符或箭头操作符。它将Lambda 分为两个部分:
    左侧:指定了Lambda 表达式需要的参数列表
    右侧:指定了Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。

     //语法一:无参,无返回值
            Runnable r = () -> System.out.println("Lambda");
            //语法二:lambda需要一个参数,但是没有返回值
            Consumer<String> consumer = (String str) -> {
                System.out.println(str);
            };
            //语法三:数据类型可以省略,因为可由编译器推断出,类型推断
            Consumer<String> consumer1 = (str) -> {
                System.out.println(str);
            };
            //语法四:lambda若只需一个参数,参数的小括号也可以省略
            Consumer<String> consumer2 = str -> {
                System.out.println(str);
            };
            //语法五:lambda需要两个或以上参数,多条执行语句,并且可以由返回值
            Comparator<Integer> comparator = (x, y) -> {
                System.out.println("实现函数式接口方法");
                return Integer.compare(x, y);
            };
            //语法六:lambda体只有一条语句时,return与大括号若有,都可以省略
            Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    类型推断
    上述Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。
    请添加图片描述

    三、函数式(Functional)接口

    如何理解函数式接口:
    Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)
    编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
    在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的
    编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
    简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是
    Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
    所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

    函数式接口举例:
    在这里插入图片描述
    自定义函数式接口:

    @FunctionalInterface
    public interface MyName {
    
        public String getName();
    }
    //使用泛型
    @FunctionalInterface
    public interface MyFun<T> {
        public T getValue(T t);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    作为参数传递 Lambda 表达式

    
            String newStr = toUpperString(
                    (str) -> str.toUpperCase(),"latte"
            );
            System.out.println(newStr);
    
        public static String toUpperString(MyFun<String> mfun, String str) {
            return mfun.getValue(str);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    进行实例化
    作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda
    表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

    Java 内置四大核心函数式接口

    请添加图片描述
    其他接口:
    请添加图片描述

    三、方法引用与构造器引用

    1、方法引用(Method References)

    • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
    • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为Lambda表达式的一个语法糖。
    • 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!
    • 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。

    如下三种主要使用情况:

    • 对象::实例方法名

    • 类::静态方法名

    • 类::实例方法名

            Consumer<String> con = (x) -> System.out.println(x);
            ///等同
            Consumer<String> con2 = System.out::println;
            
            Comparator<Integer> con1 = (x, y) -> Integer.compare(x, y);
            //等同
            Comparator<Integer> con3 = Integer::compare;
            BiPredicate<String,String> predicate = (x, y) -> x.equals(y);
            //等同
            BiPredicate<String,String> predicate2 = String::equals;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二
    个参数是需要引用方法的参数(或无参数)时:ClassName::methodName

    构造器引用
    格式: ClassName::new
    与函数式接口相结合,自动与函数式接口中方法兼容。
    可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象
    方法的参数列表一致!且方法的返回值即为构造器对应类的对象。

     Function<Integer,MyName> function = (n) -> new MyName(n);
     Function<Integer,MyName> fun = MyName::new;
    
    • 1
    • 2

    数组引用
    格式: type[] :: new

     Function<Integer,Integer[]> function1 = (n) -> new Integer[n];
     Function<Integer,Integer[]> function2 = Integer[]::new;
    
    • 1
    • 2

    四、强大的Stream API

    • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则 是 Stream API。

    • Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这
      是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程 序员的生产力,让程序员写出高效率、干净、简洁的代码。

    • Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进 行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
      使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API来并行执行操作。简言之,Stream API 提供了一种 高效且易于使用的处理数据的方式

    为什么要使用Stream API

    • 实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数
      据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要 Java层面去处理。

    • Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据 结构,而 Stream
      是有关计算的。前者是主要面向内存,存储在内存中, 后者主要是面向 CPU,通过 CPU 实现计算。

    什么是 Stream

    Stream到底是什么呢?

    是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
    “集合讲的是数据,Stream讲的是计算!”

    注意:

    ①Stream 自己不会存储元素。
    ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

    Stream 的操作三个步骤

    1. 创建 Stream

    一个数据源(如:集合、数组),获取一个流

    1. 中间操作

    一个中间操作链,对数据源的数据进行处理

    1. 终止操作(终端操作)

    一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
    请添加图片描述

    public class Employee {
    
    	private int id;
    	private String name;
    	private int age;
    	private double salary;
    
    	public int getId() {
    		return id;
    	}
    
    	public void setId(int id) {
    		this.id = id;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    
    	public void setAge(int age) {
    		this.age = age;
    	}
    
    	public double getSalary() {
    		return salary;
    	}
    
    	public void setSalary(double salary) {
    		this.salary = salary;
    	}
    
    	public Employee() {
    		System.out.println("Employee().....");
    	}
    
    	public Employee(int id) {
    		this.id = id;
    		System.out.println("Employee(int id).....");
    	}
    
    	public Employee(int id, String name) {
    		this.id = id;
    		this.name = name;
    	}
    
    	public Employee(int id, String name, int age, double salary) {
    
    		this.id = id;
    		this.name = name;
    		this.age = age;
    		this.salary = salary;
    	}
    
    	@Override
    	public String toString() {
    		return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
    	}
    
    	@Override
    	public boolean equals(Object o) {
    		if (this == o)
    			return true;
    		if (o == null || getClass() != o.getClass())
    			return false;
    
    		Employee employee = (Employee) o;
    
    		if (id != employee.id)
    			return false;
    		if (age != employee.age)
    			return false;
    		if (Double.compare(employee.salary, salary) != 0)
    			return false;
    		return name != null ? name.equals(employee.name) : employee.name == null;
    	}
    
    	@Override
    	public int hashCode() {
    		int result;
    		long temp;
    		result = id;
    		result = 31 * result + (name != null ? name.hashCode() : 0);
    		result = 31 * result + age;
    		temp = Double.doubleToLongBits(salary);
    		result = 31 * result + (int) (temp ^ (temp >>> 32));
    		return result;
    	}
    }
    public class EmployeeData {
    	
    	public static List<Employee> getEmployees(){
    		List<Employee> list = new ArrayList<>();
    		
    		list.add(new Employee(1001, "马化腾", 34, 6000.38));
    		list.add(new Employee(1002, "马云", 12, 9876.12));
    		list.add(new Employee(1003, "刘强东", 33, 3000.82));
    		list.add(new Employee(1004, "雷军", 26, 7657.37));
    		list.add(new Employee(1005, "李彦宏", 65, 5555.32));
    		list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
    		list.add(new Employee(1007, "任正非", 26, 4333.32));
    		list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
    		
    		return list;
    	}
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    创建 Stream方式一:通过集合

    Java8 中的 Collection 接口被扩展,提供了两个获取流
    的方法:

    • default Stream stream() : 返回一个顺序流

    • default Stream parallelStream() : 返回一个并行流

     public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();
    
    //        default Stream stream() : 返回一个顺序流
            Stream<Employee> stream = employees.stream();
    
    //        default Stream parallelStream() : 返回一个并行流
            Stream<Employee> parallelStream = employees.parallelStream();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建 Stream方式二:通过数组
    Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

    • static Stream stream(T[] array): 返回一个流
      重载形式,能够处理对应基本类型的数组:

    • public static IntStream stream(int[] array)

    • public static LongStream stream(long[] array)

    • public static DoubleStream stream(double[] array)

    public void test2(){
            int[] arr = new int[]{1,2,3,4,5,6};
            //调用Arrays类的static  Stream stream(T[] array): 返回一个流
            IntStream stream = Arrays.stream(arr);
    
            Employee e1 = new Employee(1001,"Tom");
            Employee e2 = new Employee(1002,"Jerry");
            Employee[] arr1 = new Employee[]{e1,e2};
            Stream<Employee> stream1 = Arrays.stream(arr1);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建 Stream方式三:通过Stream的of()

    可以调用Stream类静态方法 of(), 通过显示值创建一个
    流。它可以接收任意数量的参数。

    • public static Stream of(T… values) : 返回一个流
     public void test3(){
    
            Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建 Stream方式四:创建无限流

    可以使用静态方法 Stream.iterate() 和 Stream.generate(),
    创建无限流。

    • 迭代
    • public static Stream iterate(final T seed, final UnaryOperator f)
    • 生成
    • public static Stream generate(Supplier s)
    // 创建无限流
    @Test
    public void test() {
    // 迭代
    // public static Stream iterate(final T seed, final
    // UnaryOperator f)
    Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
    stream.limit(10).forEach(System.out::println);
    // 生成
    // public static Stream generate(Supplier s)
    Stream<Double> stream1 = Stream.generate(Math::random);
    stream1.limit(10).forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Stream 的中间操作

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止
    操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全
    部处理,称为“惰性求值”。

    1. 筛选与切片请添加图片描述
     public void test1(){
            List<Employee> list = EmployeeData.getEmployees();
    //        filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
            Stream<Employee> stream = list.stream();
            //查询员工表中薪资大于7000的员工信息
            stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
    
            System.out.println();
    //        limit(n)——截断流,使其元素不超过给定数量。
            list.stream().limit(3).forEach(System.out::println);
            System.out.println();
    
    //        skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
            list.stream().skip(3).forEach(System.out::println);
    
            System.out.println();
    //        distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    
            list.add(new Employee(1010,"刘强东",40,8000));
            list.add(new Employee(1010,"刘强东",41,8000));
            list.add(new Employee(1010,"刘强东",40,8000));
            list.add(new Employee(1010,"刘强东",40,8000));
            list.add(new Employee(1010,"刘强东",40,8000));
    
    //        System.out.println(list);
    
            list.stream().distinct().forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. 映 射请添加图片描述
    public void test2(){
    //        map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
            List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
            list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
    
    //        获取员工姓名长度大于3的员工的姓名。
            List<Employee> employees = EmployeeData.getEmployees();
            Stream<String> namesStream = employees.stream().map(Employee::getName);
            namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
            System.out.println();
            //
            Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
            streamStream.forEach(s ->{
                s.forEach(System.out::println);
            });
            System.out.println();
    //        flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
            Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream);
            characterStream.forEach(System.out::println);
    
        }
    
        //将字符串中的多个字符构成的集合转换为对应的Stream的实例
        public static Stream<Character> fromStringToStream(String str){//aa
            ArrayList<Character> list = new ArrayList<>();
            for(Character c : str.toCharArray()){
                list.add(c);
            }
           return list.stream();
    
        }
    
    
    
        @Test
        public void test3(){
            ArrayList list1 = new ArrayList();
            list1.add(1);
            list1.add(2);
            list1.add(3);
    
            ArrayList list2 = new ArrayList();
            list2.add(4);
            list2.add(5);
            list2.add(6);
    
    //        list1.add(list2);
            list1.addAll(list2);
            System.out.println(list1);
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    1. 排序
      在这里插入图片描述
    public void test4(){
    //        sorted()——自然排序
            List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
            list.stream().sorted().forEach(System.out::println);
            //抛异常,原因:Employee没有实现Comparable接口
    //        List employees = EmployeeData.getEmployees();
    //        employees.stream().sorted().forEach(System.out::println);
    
    
    //        sorted(Comparator com)——定制排序
    
            List<Employee> employees = EmployeeData.getEmployees();
            employees.stream().sorted( (e1,e2) -> {
    
               int ageValue = Integer.compare(e1.getAge(),e2.getAge());
               if(ageValue != 0){
                   return ageValue;
               }else{
                   return -Double.compare(e1.getSalary(),e2.getSalary());
               }
    
            }).forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Stream 的终止操作

    • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例 如:List、Integer,甚至是 void 。

    • 流进行了终止操作后,不能再次使用。

    1. 匹配与查找请添加图片描述

    请添加图片描述

    public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();
    
    //        allMatch(Predicate p)——检查是否匹配所有元素。
    //         是否所有的员工的年龄都大于18
            boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
            System.out.println(allMatch);
    
    //        anyMatch(Predicate p)——检查是否至少匹配一个元素。
    //        是否存在员工的工资大于 10000
            boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
            System.out.println(anyMatch);
    
    //        noneMatch(Predicate p)——检查是否没有匹配的元素。
    //      是否存在员工姓“雷”
            boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
            System.out.println(noneMatch);
    //        findFirst——返回第一个元素
            Optional<Employee> employee = employees.stream().findFirst();
            System.out.println(employee);
    //        findAny——返回当前流中的任意元素
            Optional<Employee> employee1 = employees.parallelStream().findAny();
            System.out.println(employee1);
    
        }
        public void test2(){
            List<Employee> employees = EmployeeData.getEmployees();
            // count——返回流中元素的总个数
            long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
            System.out.println(count);
    //        max(Comparator c)——返回流中最大值
    //     返回最高的工资:
            Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
            Optional<Double> maxSalary = salaryStream.max(Double::compare);
            System.out.println(maxSalary);
    //        min(Comparator c)——返回流中最小值
    //  返回最低工资的员工
            Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
            System.out.println(employee);
            System.out.println();
    //     forEach(Consumer c)——内部迭代
            employees.stream().forEach(System.out::println);
    
            //使用集合的遍历操作
            employees.forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    1. 归约请添加图片描述
      备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
    public void test3(){
    //        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
    //     计算1-10的自然数的和
            List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
            Integer sum = list.stream().reduce(0, Integer::sum);
            System.out.println(sum);
    
    
    //        reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional
    //   计算公司所有员工工资的总和
            List<Employee> employees = EmployeeData.getEmployees();
            Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
    //        Optional sumMoney = salaryStream.reduce(Double::sum);
            Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
            System.out.println(sumMoney.get());
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 收集请添加图片描述
      Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、
      Map)。
      另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,
      具体方法与实例如下表:
      请添加图片描述
      请添加图片描述
    public void test(){
    //        collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
    //  查找工资大于6000的员工,结果返回为一个List或Set
    
            List<Employee> employees = EmployeeData.getEmployees();
            List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
    
            employeeList.forEach(System.out::println);
            System.out.println();
            Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
    
            employeeSet.forEach(System.out::println);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    五、 Optional类

    • 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类, Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代 码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

    • Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表
      这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不 存在,现在 Optional可以更好的表达这个概念。并且可以避免空指针异常。

    • Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在
      则isPresent()方法会返回true,调用get()方法会返回该对象

    • Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

    • 创建Optional类对象的方法:
      Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
      Optional.empty() : 创建一个空的 Optional 实例
      Optional.ofNullable(T t):t可以为null

    • 判断Optional容器中是否包含对象:
      boolean isPresent() : 判断是否包含对象
      void ifPresent(Consumer consumer) :如果有值,就执行Consumer
      接口的实现代码,并且该值会作为参数传给它。

    • 获取Optional容器的对象:
      T get(): 如果调用对象包含值,返回该值,否则抛异常
      T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
      T orElseGet(Supplier other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
      T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常

    @Test
    public void test() {
    Boy b = new Boy("张三");
    Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
    // 如果女朋友存在就打印女朋友的信息
    opt.ifPresent(System.out::println);
    }
    @Test
    public void test1() {
    Boy b = new Boy("张三");
    Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
    // 如果有女朋友就返回他的女朋友,否则只能欣赏“嫦娥”了
    Girl girl = opt.orElse(new Girl("嫦娥"));
    System.out.println("他的女朋友是:" + girl.getName());
    }
    @Test
    public void test2(){
    Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
    //判断opt中员工对象是否满足条件,如果满足就保留,否则返回空
    Optional<Employee> emp = opt.filter(e -> e.getSalary()>10000);
    System.out.println(emp);
    }
    @Test
    public void test3(){
    Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
    //如果opt中员工对象不为空,就涨薪10%
    Optional<Employee> emp = opt.map(e -> 
    {e.setSalary(e.getSalary()%1.1);return e;});
    System.out.println(emp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    六、总结

    主要涉及Java8的新特性,Java8新增加的类还有接口的新增加的规则没有编写。
    在这里插入图片描述

  • 相关阅读:
    【整数规划】
    JAVA计算机毕业设计助农脱贫系统Mybatis+源码+数据库+lw文档+系统+调试部署
    管理项目的人——日常行为
    【STM32】CRC(循环冗余校验)
    java调用天地图api获取地理信息
    刷题笔记day15-二叉树层序遍历
    Vue Router的进阶
    express-generator快速构建node后端项目
    基于SpringBoot的在线学生请假管理系统的设计与实现毕业设计源码060935
    【SwiftUI模块】004、SwiftUI-<探探App>喜欢手势卡片
  • 原文地址:https://blog.csdn.net/qq_43803285/article/details/126397158