Java8-新特性

1.Lambda表達式

爲何使用Lambda表達式?數據庫

Lambda 是一個匿名函數,咱們能夠把Lambda表達式理解爲是一段能夠傳遞的代碼。能夠寫出更簡潔、高效的代碼。
 設計模式

1.1 Lambda初體驗

咱們先來看一段匿名內部類的代碼 使用Lambda表達式後的樣子api

//匿名內部類
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        
        TreeSet<Integer> treeSet = new TreeSet<>(com);

        //使用了Lambda式後的匿名內部類
        Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
        TreeSet<Integer> treeSet = new TreeSet<>(com);

 
準備工做: 建立一個Employee 實體類數組

public class Employee {
    
    private String name;
    private Integer age;
    private Double salary;
    private Status status;
    
    //get\set\equals\hashcode方法
    `````````````
    `````````````
    public enum Status{
        FREE,BUSY,VACATION;
    }
    
    
}

 

接下來假設咱們有一個需求: 獲取公司員工年齡大於等於35的員工信息app

那麼咱們可能須要這麼幹dom

public class TestLambda {
    
    List<Employee> list = Arrays.asList(
            new Employee("張三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.99),
            new Employee("趙六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    //傳統方式
    @Test
    public void test1(){
        List<Employee> empList = filterEmployeeByAge(list);
        
        for (Employee e:empList) {
            System.out.println(e);
        }
        
    }
    
    public List<Employee> filterEmployeeByAge(List<Employee> list){
        List<Employee> emps = new ArrayList<>();
        
        for(Employee e:list){
            if(e != null && e.getAge() >= 35)
                emps.add(e);
        }
        return emps;
    }
    
}

 

而後來了第二個需求:獲取工資大於的5000的員工,而此時原來的方法可能被別的方法引用着ide

因此咱們又這麼幹,新加了一個方法知足第二個需求函數

public List<Employee> filterEmployeeBySalary(List<Employee> list){
        List<Employee> emps = new ArrayList<>();
        
        for(Employee e:list){
            if(e != null && e.getSalary() >= 5000)
                emps.add(e);
        }
        return emps;
}

 

那麼如今有一個問題,若是接下來有第3、第四或第五個需求,咱們須要建立第3、第4、第五個方法麼優化

這好像是一件十分愚蠢的事

因此接下來咱們使用策略模式來作一次優化 , 那這跟Lambda表達式有半毛錢關係麼,別急,耐心看下去

public interface MyPredicate<T> {
    
    public boolean test(T t);
    
}
public class FilterEmployeeByAge implements MyPredicate<Employee>{

    @Override
    public boolean test(Employee t) {
        return t == null ? false:t.getAge() >= 35;
    }
    
}
public class TestLambda {
    
    List<Employee> list = Arrays.asList(
            new Employee("張三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.99),
            new Employee("趙六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    
    @Test
    public void test2(){
        List<Employee> empsList = filterEmployee(list,new FilterEmployeeByAge());
        for(Employee e:empsList){
            System.out.println(e);
        }
    }
    
    //優化方式一:策略設計模式
    public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
        List<Employee> emps = new ArrayList<>();
        
        for (Employee e:list) {
            if(mp.test(e)){
                emps.add(e);
            }
        }
        return emps;
    }
    
    
}

 
不知道你們看明白了沒有,若是我有新的需求,我只須要去改變,個人實現接口就能夠了,無需建立新的方法

可是有人可能會說,好麻煩呀,我每次想用一次filterEmployee方法去完成不一樣的需求,我就務必要建立一個新的接口實現類,並且實現類中,還就一個方法,一點也不可愛

好的,接下來咱們這麼幹,使用匿名內部類

@Test
    public void test3(){
        //匿名內部類
        List<Employee> empsList = filterEmployee(list,new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee t) {
                return t == null ? false:t.getSalary() < 5000 ;
            }
        });
        
        for(Employee em:empsList){
            System.out.println(em);
        }

    }
    
    //優化方式二:策略設計模式
    public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> mp){
        List<Employee> emps = new ArrayList<>();
        
        for (Employee e:list) {
            if(mp.test(e)){
                emps.add(e);
            }
        }
        return emps;
    }

 
接下來你們應該想到了點什麼,匿名內部類 到 Lambda

@Test
    public void test4(){
        List<Employee> empsList = filterEmployee(list,(e) -> e == null ? false:e.getSalary() < 5000 );
        empsList.forEach(System.out::println);

    }

 

1.2 Lambda表達式的基礎語法

Lambda表達式的基礎語法: Java8中引入了一個新的操做符 "—>" 該操做符稱爲箭頭操做符 或 Lambda操做符,箭頭操做符將Lambda表達式 拆分紅兩個部分

左側:Lambda表達式的參數列表

右側:Lambda表達式中所需執行的功能 即Lambda體
 

語法格式一:無參數,無返回值

​ Runnable ru = () -> System.out.println("實現Runnable的接口的Lambda");

​ ru.run();

語法格式二:有一個參數,且無返回值

​ Consumer co = (x) -> System.out.println(x);

​ co.accept("呵呵");

語法格式三: 若只有一個參數,小括號可使用不寫

​ x -> System.out.println(x);

語法格式四: 有兩個以上的參數,有返回值,而且Lambda體中有多條語句

​ Comparator com = (x,y) -> {

​ System.out.println("函數式接口");

​ return Integer.compare(x,y);

​ };

語法格式五: Lambda體中只有一條語句,return和大括號能夠省略不寫

​ Comparator com = (x,y) -> Integer.compare(x,y);

語法格式六: Lambda 表達式的參數列表的數據類型能夠省略不寫,由於JVM能夠推斷出,數據類型,即類型推斷

​ Comparator com = (Integer x,Integer y) -> Integer.compare(x,y);

​ 注:數據類型要麼全寫 要麼全不寫

 

1.3 Lambda 表達式須要"函數式的接口"的支持

函數式接口:接口中只有一個抽象方法的接口,成爲函數式接口,可使用註解 @FunctionalInterface修飾

能夠檢查是不是函數式接口
 
Java8內置的四大函數式接口

Consumer<T> : 消費型接口 void accept(T t);

Supplier<T> : 供給型接口 T get();

Function<T, R> :函數式接口 R apply(T t);

Predicate<T> : 斷言型接口 boolean test(T t);

 

public class TestLambda3 {

    // Consumer<T> 消費型接口
    @Test
    public void test1() {
        happy(10000, x -> System.out.println("我今天花了" + x + "元"));
    }

    public void happy(double money, Consumer<Double> con) {
        con.accept(money);
    }

    // Supplier<T> 供給型接口
    @Test
    public void test2() {
        List<Integer> numList = getNumList(10,() -> (int)(100*Math.random()));
        for(Integer i:numList){
            System.out.println(i);
        }
    }

    // 需求:產生指定個數的整數 放到集合中
    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < num; i++) {
            Integer e = sup.get();
            list.add(e);
        }

        return list;
    }
    
    //Functional<T> 函數式接口
    @Test
    public void test3(){
        String string = strHandler(" 我前面後面都有空格額  ",(str) -> str.trim());
        System.out.println(string);
    }
    
    //需求:用於處理字符串
    public String strHandler(String str,Function<String, String> fuc){
        return fuc.apply(str);
    }
    
    //Predicate<T> 斷言型接口
    @Test
    public void test4(){
        List<String> list = Arrays.asList("Hello","hi","Apple","zoo","beautiful");
        List<String> result = filterStr(list, (str) -> str.length() > 3);
        for(String s:result){
            System.out.println(s);
        }
    }
    //需求:將知足條件的字符串放入集合
    public List<String> filterStr(List<String> list,Predicate<String> pre){
        List<String> strList = new ArrayList<>();
        
        for(String str:list){
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }
    
}

 
固然還有其餘函數式接口

 

1.4 Lambda體中的方法引用與構造器引用

(一)、方法引用: 若Lambda 體中的內容有方法已經實現了,咱們可使用 "方法引用",(能夠理解方法引用爲Lambda的另外一種表現形式)

主要有三種語法格式:

對象 : : 實例方法名

類 : : 靜態方法名

類 : : 實例方法名

注意:

①.Lambda 體中調用方法的參數列表與返回值類型,要與函數式接口中抽象方法的函數列表和返回值類型保持一致

②.若 Lambda 參數列表中的第一個參數是 實例方法的調用者,而第二個參數是實例方法的參數時,可使用ClassName : : methodName

(二)、構造器引用:

格式: ClassName : : New

注意: 須要調用的構造器的參數列表要與函數式接口中抽象方法的參數列表保持一致!

(三)、數組引用

格式: Type[ ]::new
 

public class TestMethodRef {
    
    //對象::實例方法名
    @Test
    public void test1(){
        Consumer<String> con = (x) -> System.out.println(x);
        
        PrintStream ps = System.out;
        Consumer<String> con1 = ps::println;
        
        Consumer<String> con2 = System.out::println;
        con2.accept("abcd");

    }
    
    //對象::實例方法名
    @Test
    public void test2(){
        Employee emp = new Employee();
        Supplier<String> sup = () -> emp.getName();
        
        Supplier<String> sup1 = emp::getName;
    }
    
    //類::靜態方法名
    @Test
    public void test3(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
        
        Comparator<Integer> com1 = Integer::compare;
    }
    
    //類::實例方法名
    @Test
    public void test4(){
        BiPredicate<String, String> bp = (x,y) -> x.equals(y);
        
        BiPredicate<String, String> bp1 = String::equals;
    }
    
    //構造器引用
    @Test
    public void test5(){
        Supplier<Employee> sup = () -> new Employee();
        //構造器引用方式  調用哪一個構造器取決於 接口的參數類型
        Supplier<Employee> sup2 = Employee::new;
        Employee employee = sup2.get();
        System.out.println(employee);
    }
    
    //數組引用
    @Test
    public void test6(){
        Function<Integer, String[]> fun = (x) -> new String[x]; 
        String[] strs = fun.apply(10);
        System.out.println(strs.length);
        
        Function<Integer, String[]> fun2 = String[]::new;
        String[] apply = fun2.apply(20);
        System.out.println(apply.length);
    }
}

 

2.Stream API

簡介:

Java8中有兩大最爲重要的改變。第一個是 Lambda 表達式;另一個則是 Stream API(java.util.stream.*)。

Stream 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。

使用Stream API 對集合數據進行操做,就相似於使用 SQL 執行的數據庫查詢。也可使用 Stream API 來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式

 
流(Stream) 究竟是什麼?

是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列.

「集合講的是數據,流講的是計算!」

注意:

①.Stream 本身不會存儲元素

②.Stream 不會改變源對象。相反它們會返回一個持有對象的結果

③.Stream 操做是延遲執行的,這意味着它們會等到須要結果的時候纔會執行

Stream的三個操做步驟

1. 建立 Stream

2. 中間操做

3. 終止操做

 

2.1 建立Stream

public class TestStreamAPI {
    
    //建立Stream
    @Test
    public void test1(){
        //1.能夠經過 Collection 系列集合提供的stream() 或 parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        
        //2.經過 Arrays 中的靜態方法 stream() 獲取數組流
        Employee[] emps = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);
        
        //3.經過 Stream 類中的靜態方法 of()
        Stream<String> stream3 = Stream.of("aa","bb","cc");
        
        //4.建立無限流
        //迭代
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x+2);
        //stream4.forEach(System.out::println);
        
        //生成
        Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
    }
    
}

 

2.2.中間操做

public class TestStreamAPI {
    
    
    List<Employee> employees = Arrays.asList(
            new Employee("張三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.99),
            new Employee("王五", 50, 6666.99),
            new Employee("王五", 50, 6666.99),
            new Employee("趙六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    
    /**
     * 篩選與切片
     * filter-接收 Lambda, 從流中排除某些元素。
     * limit-截斷流,使其元素不超過給定數量
     * skip(n) - 跳過元素,返回了一個扔掉了前 n 個元素的流.若流中元素不足n個,則返回一個空流.與limit互補
     * distinct-篩選,跳過流所生成元素的 hashCode() 和 equals() 去除重複元素
     *         -此方法務必須要重寫equals 和 hashcode 方法
     */
    @Test
    public void test2(){
        //中間操做:不會執行任何操做
        Stream<Employee> stream = employees.stream().filter((e) -> {
            System.out.println("Stream API的中間操做");
            return e.getAge() > 20;
        });
        //終止操做:一次執行所有內容,即"惰性求值"
        stream.forEach(System.out::println);
    }
    
    @Test
    public void test3(){
        employees.stream().filter((e) -> {
            System.out.println("短路");
            return e.getSalary() > 3000;
        }).limit(2).forEach(System.out::println);
    }
    
    @Test
    public void test4(){
        employees.stream().filter(e -> e.getSalary() >3000).skip(2).forEach(System.out::print);
    }
    
    @Test
    public void test5(){
        employees.stream().filter((e) -> e.getSalary() >              5000).skip(2).distinct().forEach(System.out::println);
    }
    
}

 

public class TestStreamAPI2 {
    
    List<Employee> employees = Arrays.asList(
            new Employee("張三", 18, 9999.99),
            new Employee("李四", 38, 5555.99),
            new Employee("王五", 50, 6666.99),
            new Employee("王五", 50, 6666.99),
            new Employee("王五", 50, 6666.99),
            new Employee("趙六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    
    /**
     * 映射:
     * map - 接收Lambda,將元素轉換成 其餘形式或提取信息.接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素
     * flatMap - 接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後把全部流,鏈接成一個新流
     */
    @Test
    public void test2(){
        List<String> list = Arrays.asList("aa","bb","cc","dd","ee");
        
        // list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
        
        employees.stream().map(e -> e.getName()).forEach(System.out::println);
        System.out.println("--------------------------------------");
        
        Stream<Stream<Character>> stream = list.stream().map(TestStreamAPI2::filterCharacter);
        stream.forEach(sm -> sm.forEach(System.out::print));
        System.out.println("\n--------------------------------------");
        
        Stream<Character> stream2 = list.stream().flatMap(TestStreamAPI2::filterCharacter);
        stream2.forEach(System.out::print);
    }
    
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        
        for(Character ch:str.toCharArray()){
            list.add(ch);
        }
        
        return list.stream();
    }
    
    /**
     * 排序
     * sorted-天然排序(Comparable)
     * sorted(Comparable com)-定製排序(Comparator)
     */
    @Test
    public void test3(){
        List<String> list = Arrays.asList("ccc","aaa","bbb","ddd");
        list.stream().sorted().forEach(System.out::println);
        
        System.out.println("----------------------------");
        
        employees.stream().sorted((e1,e2) -> {
            if(e1.getAge().equals(e2.getAge())){
                return e1.getSalary().compareTo(e2.getSalary());
            }else{
                return e1.getAge().compareTo(e2.getAge());
            }
        }).forEach(System.out::println);;
    }
    
}

 

2.3 終止操做

/**
 * 
 * 終止操做
 *
 */
public class TestStreamAPI3 {
    
    List<Employee> employees = Arrays.asList(
            new Employee("張三", 18, 9999.99,Status.BUSY),
            new Employee("李四", 38, 5555.99,Status.FREE),
            new Employee("王五", 50, 6666.99,Status.VACATION),
            new Employee("王五", 50, 6666.99,Status.BUSY),
            new Employee("王五", 50, 6666.99,Status.BUSY),
            new Employee("趙六", 16, 3333.33,Status.FREE),
            new Employee("田七", 8, 7777.77,Status.VACATION)
    );
    
    /**
     * 查找與匹配
     * allMatch-檢查是否匹配全部元素
     * anyMatch-檢查是否至少匹配一個元素
     * noneMatch-檢查是否沒有匹配元素
     * findFirst-返回第一個元素
     * findAny-返回當前流中的任意元素
     * count-返回流中元素的總個數
     * max-返回流中最大值
     * min-返回流中最小值
     */
    @Test
    public void test1(){
        //檢查全部員工是否 都爲忙碌狀態
        boolean allMatch = employees.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(allMatch);
        //檢查是否有一個員工爲忙碌狀態
        boolean anyMatch = employees.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(anyMatch);
        //檢查是否 沒用員工處於忙碌狀態
        boolean noneMatch = employees.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(noneMatch);
        //查詢 工資排在首位的 員工
        Optional<Employee> findFirst = employees.stream().sorted((e1,e2) -> -Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
        System.out.println(findFirst.get());
        //查找任意 一個處於空閒狀態的員工
        Optional<Employee> findAny = employees.parallelStream().filter(e -> e.getStatus().equals(Status.FREE)).findAny();
        System.out.println(findAny.get());
        
        //查詢員工的個數
        long count = employees.stream().count();
        System.out.println(count);
        //查詢 拿最大工資的員工
        Optional<Employee> max = employees.stream().max((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());
        
        //查詢最小的工資數
        Optional<Double> minSalary = employees.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println(minSalary);
    }
/**
     * 歸約: reduce(T identity, BinaryOperator)/reduce(BinaryOperator)
     * -能夠將流中元素反覆結合起來獲得一個值
     */
    @Test
    public void test2() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 有起始值 全部不可能爲空 故返回值爲Integer
        Integer sum = list.stream().reduce(0, (x, y) -> (x + y));
        System.out.println(sum);

        System.out.println("------------------------");
        Optional<Double> salarySum = employees.stream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println(salarySum.get());
    }

    /**
     * 收集 collect-將流轉換成其餘形式,接收一個Collector接口的實現,用於給Stream中元素做彙總的方法
     */
    @Test
    public void test3() {
        List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
        list.forEach(System.out::println);

        System.out.println("------------------------");
        Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());

        System.out.println("------------------------");
        HashSet<String> hashSet = employees.stream().map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::print);
    }
    
    @Test
    public void test4(){
        //總數
        Long count = employees.stream().collect(Collectors.counting());
        System.out.println(count);
        
        System.out.println("-------------------------------");
        //平均值
        Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);
        //總和
        Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);
        //最大值
        Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());
        //最小值
        Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
    }
    
    //分組
    @Test
    public void test5(){
        //單級分組
        Map<Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
        //多級分組
        Map<Status, Map<String, List<Employee>>> map2 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy(e -> {
            if(((Employee)e).getAge() < 18){
                return "未成年";
            }else{
                return "成年";
            }
        })));
        System.out.println(map2);
    }
    //分區
    @Test
    public void test6(){
        Map<Boolean, List<Employee>> part = employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
        System.out.println(part);
    }
    
    @Test
    public void test7(){
        DoubleSummaryStatistics doubleSummaryStatistics = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(doubleSummaryStatistics.getSum());
        System.out.println(doubleSummaryStatistics.getAverage());
        System.out.println(doubleSummaryStatistics.getMax());
    }
    
}
相關文章
相關標籤/搜索