zoukankan      html  css  js  c++  java
  • Java8特性总结——恕我直言你可能真的不会java系列学习笔记

    Java8特性总结

    ——恕我直言你可能真的不会java系列学习笔记
    

    字母哥讲:恕我直言你可能真的不会java系列的学习笔记,以下是学习资料直达地址
    视频:https://www.bilibili.com/video/BV1sE411P7C1
    文章:https://www.kancloud.cn/hanxt/javacrazy/1568811
    主讲: java8基础特性
    恕我直言你可能真的不会java系列-lambda、streamAPI、文本块等特性深入讲解
    学习笔记源码:https://github.com/gaogushenling/lambda-StreamAPI

    01.lambda表达式会用了么

    lambda表达式表达接口函数的实现

    一、接口定义

    1、经典OOP的实现样式
    public class LambdaDemo {
        //函数定义
        public void printSomething(String something) {
            System.out.println(something);
        }
        //通过创建对象调用函数
        public static void main(String[] args) {
            LambdaDemo demo = new LambdaDemo();
            String something = "I am learning Lambda";
            demo.printSomething(something);
        }
    }
    
    2、创建功能接口,并对该接口定义抽象方法
    public class LambdaDemo {
        //抽象功能接口
        interface Printer {
            void print(String val);
        }
        //通过参数传递功能接口
        public void printSomething(String something, Printer printer) {
            printer.print(something);
        }
    }
    

    二、传统的接口函数实现方式

    public static void main(String[] args) {
        LambdaDemo demo = new LambdaDemo();
        String something = "I am using a Functional interface";
        //实现Printer接口
        Printer printer = new Printer() {
            @Override
            public void print(String val) {
                //控制台打印
                System.out.println(val);
            }
        };
        demo.printSomething(something, printer);
    }
    

    三、lambda表示式实现方式

    lambda表达式的语法:

    (param1,param2,param3 ...,paramN)-  > {   //代码块;  }
    

    首先我们知道lambda表达式,表达的是接口函数
    箭头左侧是函数的逗号分隔的形式参数列表
    箭头右侧是函数体代码

    public static void main(String[] args) {
        LambdaDemo demo = new LambdaDemo();
        String something = "I am learning Lambda";
        //实现Printer接口(请关注下面这行lambda表达式代码)
        Printer printer = (String toPrint)->{System.out.println(toPrint);};
        //调用接口打印
        demo.printSomething(something, printer);
    }
    

    例子

    package main.java;
    
    public class LambdaDemo1 {
        interface Printer{
            void printer(String val);
        }
        public void pringSomething(String something,Printer printer){
            printer.printer(something);
        }
    
        public static void main(String[] args) {
            LambdaDemo1 lambdaDemo1 = new LambdaDemo1();
            String some = "asdasasa";
    
            //不使用lambda表达式
            Printer printer = new Printer() {
                @Override
                public void printer(String val) {
                    System.out.println(val);
                }
            };
            lambdaDemo1.pringSomething(some,printer);
    
            /*1、使用lambda表达式
            *接口匿名实现类,简化函数
            * ①参数
            * ②函数体
            * */
            Printer printer1 = (String val) ->{
                System.out.println(val);
            };
    
            //1.1、进一步简化,去掉参数类型
            Printer printer2 = (val) ->{
                System.out.println(val);
            };
            //1.2、进一步简化,去掉参数括号(前提:只有一个参数)
            Printer printer3 = val ->{
                System.out.println(val);
            };
            //1.3、进一步简化,去掉函数体大括号(前提:函数体只有一行代码)
            Printer printer4 = val -> System.out.println(val);
    
            //1.4、最后可精简为如下,会自动推断lambda传入的参数类型
            lambdaDemo1.pringSomething(some,val -> System.out.println(val));
    
            //1.5、无参数传入的话,可以简写成
    //        () -> System.out.println("无参");
            /*
            * 总结:
            * 省略类型:自动推断
            * 省略括号:一个参数
            * 
            * lambda表达式表达的是接口函数,
            * 箭头左侧是函数参数,箭头右侧是函数体。
            * 函数的参数类型和返回值类型都可以省略,程序会根据接口定义的上下文自动确定数据类型。
            * */
        }
    }
    

    02.Stream代替for循环(初识Stream-API)

    认识

    Java Stream就是一个数据流经的管道,并且在管道中对数据进行操作,然后流入下一个管道。

    管道的功能包括:Filter(过滤)、Map(映射)、sort(排序)等,集合数据通过Java Stream管道处理之后,转化为另一组集合或数据输出。

    图一

    例子 Stream代替for循环

    package main.java;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class StreamDemo1 {
        public static void main(String[] args) {
            List<String> letters = Arrays.asList("e", "c", "cbn", "zxc", "caz", "d", "b", "a");
    
            //不使用StreamAPI
           /* for (String letter : letters) {
                                    //转换大写
                String temp = letter.toLowerCase();
                //排序
                //转
                //总之非常麻烦
            }*/
    
            //1、集合 使用StreamAPI
            //数组转换成流
            List<String> lettertsNew = letters.stream()
                    //过滤:找出以c开头的元素
                    .filter(s -> s.startsWith("c"))
                    //将过滤后的结果全部大写,其中::为函数引用
                    .map(String::toUpperCase)
                    //排序,可写排序规则
                    .sorted()
                    //将流转换成集合
                    .collect(Collectors.toList());
    
            System.out.println(lettertsNew);
    
            //2、数组转换成流
            String[] letters2 = {"e", "c", "cbn", "zxc", "caz", "d", "b", "a"};
            Stream.of(letters2).filter(s -> s.startsWith("c")).map(String::toUpperCase).sorted().toArray(String[]::new);
    
            //3、set转成流:集合类是一样的
            Set<String> lettersSet = new HashSet<>(letters);
            List<String> lettertsNewSet = lettersSet.stream()
                    //过滤:找出以c开头的元素
                    .filter(s -> s.startsWith("c"))
                    //将过滤后的结果全部大写,其中::为函数引用
                    .map(String::toUpperCase)
                    //排序,可写排序规则
                    .sorted()
                    //将流转换成集合
                    .collect(Collectors.toList());
    
            //4、文本文件转换成流
            Paths.get("file.text");
            try {
                Stream<String> stringStream = Files.lines(Paths.get("file.text"));
                List<String> f = stringStream
                        //过滤:找出以c开头的元素
                        .filter(s -> s.startsWith("c"))
                        //将过滤后的结果全部大写,其中::为函数引用
                        .map(String::toUpperCase)
                        //排序,可写排序规则
                        .sorted()
                        //将流转换成集合
                        .collect(Collectors.toList());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    题外话 介绍双冒号操作符

    双冒号运算就是Java中的[方法引用] Method

    [方法引用]的格式是

    类名::方法名
    

    例如

    1.表达式:
    person -> person.getName();
    可以替换成:
    Person::getName
    
    2.表达式:
    () -> new HashMap<>();
    可以替换成:
    HashMap::new
    

    方法引用的种类

    方法引用有四种,分别是:

    指向静态方法的引用

    指向某个对象的实例方法的引用

    指向某个类型的实例方法的引用

    指向构造方法的引用

    图二

    其实,JVM 本身并不支持指向方法引用,过去不支持,现在也不支持。

    Java 8 对方法引用的支持只是编译器层面的支持,虚拟机执行引擎并不了解方法引用。编译器遇到方法引用的时候,会像上面那样自动推断出程序员的意图,将方法引用还原成接口实现对象,或者更形象地说,就是把方法引用设法包装成一个接口实现对象,这样虚拟机就可以无差别地执行字节码文件而不需要管
    什么是方法引用了。

    需要注意的是,方法引用是用来简化接口实现代码的,并且凡是能够用方法引用来简化的接口,都有这样的特征:有且只有一个待实现的方法。这种接口在 Java 中有个专门的名称: 函数式接口。当你用试图用方法引用替代一个非函数式接口时,会有这样的错误提示: xxx is not a functional interface。

    3.Stream的filter与谓语逻辑

    什么是谓词逻辑?

    WHERE 和 AND 限定了主语employee是什么,那么WHERE和AND语句所代表的逻辑就是谓词逻辑

    SELECT *
    FROM employee
    WHERE age > 70
    AND gender = 'M'
    

    谓词逻辑的复用

    filter函数中lambda表达式为一次性使用的谓词逻辑。如果我们的谓词逻辑需要被多处、多场景、多代码中使用,通常将它抽取出来单独定义到它所限定的主语实体中。
    比如:将下面的谓词逻辑定义在Employee实体class中。

     public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
       public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
    

    and语法(并集)
    or语法(交集)
    negate语法(取反)

    例子

    package model;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    import java.util.function.Predicate;
    
    
    @Data
    @AllArgsConstructor
    //员工
    public class Employee {
    
        private Integer id;
        private Integer age;   //年龄
        private String gender;  //性别
        private String firstName; //名字
        private String lastName; //姓氏
    
        //Predicate接口,在英语中这个单词的意思是:谓词
        //谓词逻辑的复用如下
        //e -> e.getAge() > 70 && e.getGender().equals("M")
        public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
        public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
    }
    
    package model;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class StreamFilterPredicate {
        
        public static void main(String[] args){
            Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
            Employee e2 = new Employee(2,13,"F","Martina","Hengis");
            Employee e3 = new Employee(3,43,"M","Ricky","Martin");
            Employee e4 = new Employee(4,26,"M","Jon","Lowman");
            Employee e5 = new Employee(5,19,"F","Cristine","Maria");
            Employee e6 = new Employee(6,15,"M","David","Feezor");
            Employee e7 = new Employee(7,68,"F","Melissa","Roy");
            Employee e8 = new Employee(8,79,"M","Alex","Gussin");
            Employee e9 = new Employee(9,15,"F","Neetu","Singh");
            Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
    
            List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
            List<Employee> filtered = employees.stream()
                    //filter(写谓词逻辑) 年龄大于70 并且是男性
                    .filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
                    .collect(Collectors.toList());
    
            //使用可复用谓词逻辑
            List<Employee> filteredAnd = employees.stream()
                    .filter(Employee.ageGreaterThan70
                            .and(Employee.genderM))
                    .collect(Collectors.toList());
    
            //or
            List<Employee> filteredOr = employees.stream()
                    .filter(Employee.ageGreaterThan70
                            .or(Employee.genderM))
                    .collect(Collectors.toList());
    
            //negate 取反
            List<Employee> filteredNegate = employees.stream()
                    .filter(
                            Employee.ageGreaterThan70
                                    .or(Employee.genderM)
                                    .negate()
                    )
                    .collect(Collectors.toList());
    
    
            System.out.println("filtered="+filtered);
            System.out.println("filteredAnd="+filteredAnd);
            System.out.println("filteredOr="+filteredOr);
            System.out.println("filteredNegate="+filteredNegate);
        }
    
    }
    

    4.Stream的map数据转换(Stream管道流的map操作)

    map函数的作用就是针对管道流中的每一个数据元素进行转换操作。

    例子

    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class StreamMap1 {
        public static void main(String[] args) {
            List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
    
            //不使用Stream管道流
            List<String> alphaUpper = new ArrayList<>();
            for (String s : alpha) {
                alphaUpper.add(s.toUpperCase());
            }
            System.out.println(alphaUpper); //[MONKEY, LION, GIRAFFE, LEMUR]
    
            // 1、Stream管道流map的基础用法:使用Stream管道流
            List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
            //上面使用了方法引用,和下面的lambda表达式语法效果是一样的
            //List<String> collect = alpha.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
    
            System.out.println(collect); //[MONKEY, LION, GIRAFFE, LEMUR]
    
            //2、处理非字符串类型集合元素
            List<Integer> lengths = alpha.stream()
                    .map(String::length)
                    .collect(Collectors.toList());
    
            System.out.println(lengths); //[6, 4, 7, 5]
    
            Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                    //除了mapToInt。还有maoToLong,mapToDouble等等用法
                    .mapToInt(String::length)
                    .forEach(System.out::println);
        }
    }
    
    
    import model.Employee;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class StreamMap2 {
        public static void main(String[] args) {
            Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
            Employee e2 = new Employee(2,13,"F","Martina","Hengis");
            Employee e3 = new Employee(3,43,"M","Ricky","Martin");
            Employee e4 = new Employee(4,26,"M","Jon","Lowman");
            Employee e5 = new Employee(5,19,"F","Cristine","Maria");
            Employee e6 = new Employee(6,15,"M","David","Feezor");
            Employee e7 = new Employee(7,68,"F","Melissa","Roy");
            Employee e8 = new Employee(8,79,"M","Alex","Gussin");
            Employee e9 = new Employee(9,15,"F","Neetu","Singh");
            Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
    
            List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
            //每人涨一岁,性别换全词
            /*List<Employee> maped = employees.stream()
                .map(e -> {
                    e.setAge(e.getAge() + 1);
                    e.setGender(e.getGender().equals("M")?"male":"female");
                    return e;
                }).collect(Collectors.toList());*/
    
            List<Employee> maped = employees.stream()
                    .peek(e -> {
                        e.setAge(e.getAge() + 1);
                        e.setGender(e.getGender().equals("M")?"male":"female");
                    }).collect(Collectors.toList());
    
            System.out.println(maped);
        }
    }
    
    
    import java.util.Arrays;
    import java.util.List;
    
    public class StreamFlatMap {
        public static void main(String[] args) {
            List<String> words = Arrays.asList("hello", "word");
            words.stream()
                    .map(w -> Arrays.stream(w.split("")))    //[[h,e,l,l,o],[w,o,r,l,d]]
                    .forEach(System.out::println);
    
            //flatMap可以理解为将若干个子管道中的数据全都,平面展开到父管道中进行处理
            words.stream()
                    .flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
                    .forEach(System.out::println);
        }
    }
    
    

    5.Stream的状态与并行操作

    Stream管道流的基本操作

    1. 源操作:可以将数组、集合类、行文本文件转换成管道流Stream进行数据处理
    2. 中间操作:对Stream流中的数据进行处理,比如:过滤、数据转换等等
    3. 终端操作:作用就是将Stream管道流转换为其他的数据类型。

    图三

    中间操作:有状态与无状态

    状态通常代表公用数据,有状态就是有“公用数据”

    Limit与Skip管道数据截取
    Distinct元素去重
    Sorted排序
    串行、并行与顺序

    例子

    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class StreamState {
        public static void main(String[] args) {
            //1、Limit与Skip管道数据截取
            List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                    .limit(2)
                    .collect(Collectors.toList());
            List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                    .skip(2)
                    .collect(Collectors.toList());
            //2、Distinct元素去重
            List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                    .distinct()
                    .collect(Collectors.toList());
            //3、Sorted排序
            List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
                    .sorted()
                    .collect(Collectors.toList());
            //4、串行、并行与顺序
            //默认串行
            //并行操作parallel,操作无状态操作
            Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                    .parallel()
                    .forEach(System.out::println);
        }
    }
    
    

    结果

    Giraffe
    Lion
    Lemur
    Lion
    Monkey
    
    Process finished with exit code 0
    
    

    6.Stream性能差问号 不要人云亦云

    7.像使用SQL一样排序集合

    一、字符串List排序
    二、整数类型List排序
    三、按对象字段对List排序
    四、Comparator链对List排序

    例子

    import model.Employee;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class SortList {
    
        public static void main(String[] args) {
    
            //Collections.sort();
            //1、字符串List排序
            List<String> cities = Arrays.asList(
                    "Milan",
                    "london",
                    "San Francisco",
                    "Tokyo",
                    "New Delhi"
            );
            System.out.println(cities);
            //[Milan, london, San Francisco, Tokyo, New Delhi]
    
            //大小写不敏感的排序
            cities.sort(String.CASE_INSENSITIVE_ORDER);
            System.out.println(cities);
            //[london, Milan, New Delhi, San Francisco, Tokyo]
    
            //大小写敏感的排序
            cities.sort(Comparator.naturalOrder());
            System.out.println(cities);
            //[Milan, New Delhi, San Francisco, Tokyo, london]
    
            //同样我们可以把排序器Comparator用在Stream管道流中。
            cities.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
            //Milan
            //New Delhi
            //San Francisco
            //Tokyo
            //london
    
            //2、整数类型List排序
            List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
            System.out.println(numbers); //[6, 2, 1, 4, 9]
    
            numbers.sort(Comparator.naturalOrder());  //自然排序
            System.out.println(numbers); //[1, 2, 4, 6, 9]
    
            numbers.sort(Comparator.reverseOrder()); //倒序排序
            System.out.println(numbers);  //[9, 6, 4, 2, 1]
    
            //3、按对象字段对List<Object>排序
            Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
            Employee e2 = new Employee(2,13,"F","Martina","Hengis");
            Employee e3 = new Employee(3,43,"M","Ricky","Martin");
            Employee e4 = new Employee(4,26,"M","Jon","Lowman");
            Employee e5 = new Employee(5,19,"F","Cristine","Maria");
            Employee e6 = new Employee(6,15,"M","David","Feezor");
            Employee e7 = new Employee(7,68,"F","Melissa","Roy");
            Employee e8 = new Employee(8,79,"M","Alex","Gussin");
            Employee e9 = new Employee(9,15,"F","Neetu","Singh");
            Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
            List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
    
            //4、Comparator链对List<Object>排序
            employees.sort(Comparator.comparing(Employee::getAge));
            employees.forEach(System.out::println);
    
            //先按性别,再按年龄倒序
            //如果我们希望List按照年龄age的倒序排序,就使用reversed()方法
            employees.sort(
                    Comparator.comparing(Employee::getGender)
                            .thenComparing(Employee::getAge)
                            .reversed()
            );
            employees.forEach(System.out::println);
    
            //都是正序 ,不加reversed
            //都是倒序,最后面加一个reserved
            //先是倒序(加reserved),然后正序
            //先是正序(加reserved),然后倒序(加reserved)
        }
    }
    
    

    结果

    [Milan, london, San Francisco, Tokyo, New Delhi]
    [london, Milan, New Delhi, San Francisco, Tokyo]
    [Milan, New Delhi, San Francisco, Tokyo, london]
    Milan
    New Delhi
    San Francisco
    Tokyo
    london
    [6, 2, 1, 4, 9]
    [1, 2, 4, 6, 9]
    [9, 6, 4, 2, 1]
    Employee(id=8, age=79, gender=M, firstName=Alex, lastName=Gussin)
    Employee(id=10, age=45, gender=M, firstName=Naveen, lastName=Jain)
    Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
    Employee(id=4, age=26, gender=M, firstName=Jon, lastName=Lowman)
    Employee(id=1, age=23, gender=M, firstName=Rick, lastName=Beethovan)
    Employee(id=6, age=15, gender=M, firstName=David, lastName=Feezor)
    Employee(id=7, age=68, gender=F, firstName=Melissa, lastName=Roy)
    Employee(id=5, age=19, gender=F, firstName=Cristine, lastName=Maria)
    Employee(id=9, age=15, gender=F, firstName=Neetu, lastName=Singh)
    Employee(id=2, age=13, gender=F, firstName=Martina, lastName=Hengis)
    
    Process finished with exit code 0
    
    

    8.函数式接口Comparator

    含义

    所谓的函数式接口,实际上就是接口里面只能有一个抽象方法的接口。

    二、函数式接口的特点

    • 接口有且仅有一个抽象方法,如上图的抽象方法compare
    • 允许定义静态非抽象方法
    • 允许定义默认defalut非抽象方法(default方法也是java8才有的,见下文)
    • 允许java.lang.Object中的public方法,如上图的方法equals。
    • FunctionInterface注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错

    甚至可以说:函数式接口是专门为lambda表达式准备的,lambda表达式是只实现接口中唯一的抽象方法的匿名实现类。

    三、default关键字

    顺便讲一下default关键字,在java8之前

    接口是不能有方法的实现,所有方法全都是抽象方法
    实现接口就必须实现接口里面的所有方法

    这就导致一个问题:当一个接口有很多的实现类的时候,修改这个接口就变成了一个非常麻烦的事,需要修改这个接口的所有实现类。

    这个问题困扰了java工程师许久,不过在java8中这个问题得到了解决,没错就是default方法

    default方法可以有自己的默认实现,即有方法体。
    接口实现类可以不去实现default方法,并且可以使用default方法。

    四、JDK中的函数式接口举例

    java.lang.Runnable,
    java.util.Comparator,
    java.util.concurrent.Callable
    java.util.function包下的接口,如Consumer、Predicate、Supplier等

    五、自定义Comparator排序

     //8、自定义Comparator排序
           /* employees.sort(new Comparator<Employee>() {
                @Override
                public int compare(Employee em1, Employee em2) {
                    if(em1.getAge() == em2.getAge()){
                        return 0;
                    }
                    return em1.getAge() - em2.getAge() > 0 ? -1:1;
                }
            });*/
            //简化
            employees.sort((em1,em2) -> {
                if(em1.getAge() == em2.getAge()){
                    return 0;
                }
                return em1.getAge() - em2.getAge() > 0 ? -1:1;
            });
            employees.forEach(System.out::println);
    

    9.Stream查找与匹配元素

    问题

    在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如:
    是否包含某一个“匹配规则”的元素
    是否所有的元素都符合某一个“匹配规则”
    是否所有元素都不符合某一个“匹配规则”
    查找第一个符合“匹配规则”的元素
    查找任意一个符合“匹配规则”的元素

    例子

    import model.Employee;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    public class Matvhfind {
        public static void main(String[] args) {
            Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
            Employee e2 = new Employee(2,13,"F","Martina","Hengis");
            Employee e3 = new Employee(3,43,"M","Ricky","Martin");
            Employee e4 = new Employee(4,26,"M","Jon","Lowman");
            Employee e5 = new Employee(5,19,"F","Cristine","Maria");
            Employee e6 = new Employee(6,15,"M","David","Feezor");
            Employee e7 = new Employee(7,68,"F","Melissa","Roy");
            Employee e8 = new Employee(8,79,"M","Alex","Gussin");
            Employee e9 = new Employee(9,15,"F","Neetu","Singh");
            Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
            List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
            //不用Stream API实现,查找员工列表中是否包含年龄大于70的员工
            boolean isExistAgeThan70 = false;
            for(Employee employee:employees){
                if(employee.getAge() > 70){
                    isExistAgeThan70 = true;
                    break;
                }
            }
            System.out.println(isExistAgeThan70);
    
            //1、第一个匹配规则函数:anyMatch,判断Stream流中是否 包含 某一个“匹配规则”的元素。
            // 这个匹配规则可以是lambda表达式或者谓词。
    
            //使用Stream API
            boolean isExistAgeThan702 = employees.stream().anyMatch(Employee.ageGreaterThan70);
            System.out.println(isExistAgeThan702);
            //将谓词逻辑换成lambda表达式
            boolean isExistAgeThan72 = employees.stream().anyMatch(e -> e.getAge() > 72);
            System.out.println(isExistAgeThan72);
    
            //2.1、allMatch匹配规则函数:判断是够Stream流中的 所有元素都 符合某一个"匹配规则"。
            //是否所有员工的年龄都大于10岁
            boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);
            System.out.println(isExistAgeThan10);
    
            //2.2、noneMatch匹配规则函数:判断是否Stream流中的 所有元素都不 符合某一个"匹配规则"。
            //是否不存在小于18岁的员工
            boolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);
            System.out.println(isExistAgeLess18);
    
            /**
             *  3、元素查找与Optional
             * Optional类代表一个值存在或者不存在。在java8中引入,这样就不用返回null了。
             *
             * isPresent() 将在 Optional 包含值的时候返回 true , 否则返回 false 。
             * ifPresent(Consumer block) 会在值存在的时候执行给定的代码块。我们在第3章
             * 介绍了 Consumer 函数式接口;它让你传递一个接收 T 类型参数,并返回 void 的Lambda
             * 表达式。
             * T get() 会在值存在时返回值,否则?出一个 NoSuchElement 异常。
             * T orElse(T other) 会在值存在时返回值,否则返回一个默认值。
             * 关于Optinal的各种函数用法请观看视频!B站观看地址
             *
             * findFirst用于查找第一个符合“匹配规则”的元素,返回值为Optional
             * findAny用于查找任意一个符合“匹配规则”的元素,返回值为Optional
             */
            //从列表中按照顺序查找第一个年龄大于40的员工
            Optional<Employee> employeeOptional
                    =  employees.stream().filter(e -> e.getAge() > 40).findFirst();
            System.out.println(employeeOptional.get());
    
            //isPresent是否存在
           boolean is
                    =  employees.stream().filter(e -> e.getAge() > 40).findFirst().isPresent();
            System.out.println(is);
    
            //ifPresent如果存在
            employees.stream().filter(e -> e.getAge() > 40).findFirst().ifPresent(
                            e -> System.out.println(e)
            );
    
            //orElse不存在给默认值
            Employee employeeOptionalOrElse =
            employees.stream().filter(e -> e.getAge() > 90).findFirst().orElse(
                            new Employee(0,0,"F","","")
            );
            System.out.println(employeeOptionalOrElse);
    
            //findAny 找任意一个
            Employee employeeOptionalOrElseFindAny =
                    employees.stream().filter(e -> e.getAge() > 90).findAny().orElse(
                            new Employee(0,0,"F","","")
                    );
            System.out.println(employeeOptionalOrElseFindAny);
        }
    }
    
    

    结果

    true
    true
    true
    true
    false
    Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
    true
    Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)
    Employee(id=0, age=0, gender=F, firstName=, lastName=)
    Employee(id=0, age=0, gender=F, firstName=, lastName=)
    
    Process finished with exit code 0
    
    

    10.Stream集合元素归约

    Stream API为我们提供了Stream.reduce用来实现集合元素的归约。reduce函数有三个参数:

    • Identity标识:一个元素,它是归约操作的初始值,如果流为空,则为默认结果。
    • Accumulator累加器:具有两个参数的函数:归约运算的部分结果和流的下一个元素。
    • Combiner合并器(可选):当归约并行化时,或当累加器参数的类型与累加器实现的类型不匹配时,用于合并归约操作的部分结果的函数。

    图四

    注意观察上面的图,我们先来理解累加器:

    • 阶段累加结果作为累加器的第一个参数
    • 集合遍历元素作为累加器的第二个参数

    Integer类型归约
    String类型归约
    复杂对象归约
    Combiner合并器的使用
    对于大数据量的集合元素归约计算,更能体现出Stream并行流计算的威力。

    图五

    例子

    import model.Employee;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * Stream API为我们提供了Stream.reduce用来实现集合元素的归约。reduce函数有三个参数:
     *
     * Identity标识:一个元素,它是归约操作的初始值,如果流为空,则为默认结果。
     * Accumulator累加器:具有两个参数的函数:归约运算的部分结果和流的下一个元素。
     * Combiner合并器(可选):当归约并行化时,或当累加器参数的类型与累加器实现的类型不匹配时,用于合并归约操作的部分结果的函数。
     * 理解累加器:
     * 阶段累加结果作为累加器的第一个参数
     * 集合遍历元素作为累加器的第二个参数
     */
    public class MatchFindDemo {
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    
            //1、Integer类型归约
            //reduce初始值为0,累加器可以是lambda表达式,也可以是方法引用。
            int result = numbers
                    .stream()
                    .reduce(0, (subtotal, element) -> subtotal + element);
            System.out.println(result);  //21
    
            int result12 = numbers
                    .stream()
                    .reduce(0, Integer::sum);
            System.out.println(result12); //21
    
            //2、String类型归约
            //不仅可以归约Integer类型,只要累加器参数类型能够匹配,可以对任何类型的集合进行归约计算。
            List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
            String result21 = letters
                    .stream()
                    .reduce("", (partialString, element) -> partialString + element);
            System.out.println(result21);  //abcde
    
    
            String result22 = letters
                    .stream()
                    .reduce("", String::concat);
            System.out.println(result22);  //ancde
    
            //3、复杂对象归约
            //计算所有的员工的年龄总和。
            Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
            Employee e2 = new Employee(2,13,"F","Martina","Hengis");
            Employee e3 = new Employee(3,43,"M","Ricky","Martin");
            Employee e4 = new Employee(4,26,"M","Jon","Lowman");
            Employee e5 = new Employee(5,19,"F","Cristine","Maria");
            Employee e6 = new Employee(6,15,"M","David","Feezor");
            Employee e7 = new Employee(7,68,"F","Melissa","Roy");
            Employee e8 = new Employee(8,79,"M","Alex","Gussin");
            Employee e9 = new Employee(9,15,"F","Neetu","Singh");
            Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
    
            List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
            //先用map将Stream流中的元素由Employee类型处理为Integer类型(age)。
            //然后对Stream流中的Integer类型进行归约
            Integer total = employees.stream().map(Employee::getAge).reduce(0,Integer::sum);
            System.out.println(total); //346
    
            //4、Combiner合并器的使用
            /*
            * 除了使用map函数实现类型转换后的集合归约,我们还可以用Combiner合并器来实现,这里第一次使用到了Combiner合并器。
            * 因为Stream流中的元素是Employee,累加器的返回值是Integer,所以二者的类型不匹配。
            * 这种情况下可以使用Combiner合并器对累加器的结果进行二次归约,相当于做了类型转换
            * */
            Integer total3 = employees.stream()
                    .reduce(0,(totalAge,emp) -> totalAge + emp.getAge(),Integer::sum); //注意这里reduce方法有三个参数
            System.out.println(total3); //346
    
            //5、*并行流数据归约(使用合并器)
            //在进行并行流计算的时候,可能会将集合元素分成多个组计算。为了更快的将分组计算结果累加,可以使用合并器。
            Integer total2 = employees
                    .parallelStream()
                    .map(Employee::getAge)
                    .reduce(0,Integer::sum,Integer::sum);  //注意这里reduce方法有三个参数即合并器
    
            System.out.println(total2); //346
        }
    }
    
    

    结果

    21
    21
    abcde
    abcde
    346
    346
    346
    
    Process finished with exit code 0
    
    

    11.StreamAPI终端操作

    12.java8如何排序Map

    13.Stream流逐行文件处理

    14.java8-forEach(持续发布中)

  • 相关阅读:
    关于“每日代码系列”以及后续计划
    每日代码系列(22)
    每日代码系列(21)
    mvcc
    父进程是1号进程产生大量的僵尸进程的解决方案
    nginx学习之路
    Zookeeper Curator 分布式锁
    jvm垃圾收集器汇总
    MySql分库分表以及相关问题
    Https交互原理
  • 原文地址:https://www.cnblogs.com/gaogushenling/p/14186582.html
Copyright © 2011-2022 走看看