JDK8 新特性

Java 8

如今 Java 按照週期發佈,每六個月就更新一次。所謂 「小步快跑,快速迭代」java

JDK8 是 Java 語言開發的一個主要版本,2014年 3月發佈,是 JDK5 以來最具革命性的版本。數據庫

新特性簡介:api

  1. 速度更快 (HashMap 的數據結構更改)數組

  2. 代碼更少 (Lambda 表達式)緩存

  3. Stream API數據結構

  4. 便於並行app

  5. 減小控制針異常 (Optional)dom

  6. Nashorn引擎,容許在 JVM 上運行 JS 應用 (使用 jjs.exe JDK 11中沒有了)ide




Lambda 表達式

Lambda 表達式是對象,而不是函數。函數

// 之前建立接口實現類的對象

public interface MyInterface {
    void testMethod();
}


class MyClass implements MyInterface{

    @Override
    public void testMethod() {
        System.out.println("重寫接口");
    }
}


class Demo {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        mc.testMethod();
    }
}
// 使用匿名內部類建立對象

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = new MyInterface() {
            @Override
            public void testMethod() {
                System.out.println("Test");
            }
        };

        ms.testMethod();
    }
}
// 利用 Lambda 表達式

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = () -> System.out.println("Lambda");
        ms.testMethod();
    }
}

Lambda 本質就是 Java 中接口的一個實例,即接口的實現類的具體的對象。


語法:
-> :         箭頭操做符
-> 左側:      Lambda 形參列表 對應接口中抽象方法的形參列表
-> 右側:      重寫方法的,方法體的具體內容
// 無參,無返回值. 方法體中只有一條語句, 花括號能夠省略
MyInterface my = () -> {System.out.println("PS");};
my.testMethod();

// 有一個參數, 無返回值
MyInterface my = (int x) -> System.out.println("PS" + x);
my.testMethod(12);

// 類型推斷, 能夠去掉參數類型
MyInterface my = (x) -> System.out.println("ps" + x);
my.testMethod(12);

// 一個參數的時候 小括號也能不寫
MyInterface my = x -> System.out.println("ps" + x);
my.testMethod(12);
// 多個參數且帶返回值
public interface MyInterface {
    String testMethod(int age, int num);
}

class Demo {
    public static void main(String[] args) {

        MyInterface my = (x,y) -> {
            System.out.println("Test");
            return "Hello" + "ps" + x;
        };
        System.out.println( my.testMethod(12,17));
    }
}
// 若是隻剩一個 return 的時候
MyInterface my = (x,y) -> {
    return "Hello" + "ps" + x;
};


// 能夠直接這樣
MyInterface my = (x,y) -> "Hello" + "ps" + x;




函數式接口

Lambda 必須依賴一個接口,只有一個抽象方法的接口,稱爲函數式接口

@FunctionalInterface 此註解在接口中有多個抽象方法時,便會爆紅。不過除了 Object 類中的抽象方法。

JDK8 中新加入了一個包 java.util.function ,這個包裏面有幾個經常使用的函數式接口:

  1. Consumer 消費型接口
  2. Function 函數型接口
  3. Predicate 判定型接口
  4. Supplier 供給型接口

這些接口功能就是,不用你再去建立新接口,按照名字的語義。直接用它的就好了,好比:

public static void main(String[] args) {

    Consumer<Double> c = new Consumer<Double>() {
        @Override
        public void accept(Double o) {
            System.out.println("accept");
        }
    };

    c.accept(1.00);
}


// 簡潔一下
Consumer<Double> c = o -> System.out.println("accept");
public static void main(String[] args) {

    List<String> list = Arrays.asList("asss", "aaaa", "vvvv", "vvv");

// 簡化前
//        List<String> result = filterString(list, new Predicate<String>() {
//            @Override
//            public boolean test(String o) {
//                return o.length() < 4;
//            }
//        });


// 簡化後
    List<String> result = filterString(list, o -> o.length() < 4);

    System.out.println(list);
    System.out.println(result);
}

public static List<String> filterString(List<String> waitTest, Predicate<String> predicate) {
    List<String> filterList = new ArrayList<>();
    for (String item : waitTest) {
        if (predicate.test(item)) {
            filterList.add(item);
        }
    }
    return filterList;
}




方法引用

當要傳遞給 Lambda 體的操做,已經有實現的方法了,可使用方法引用。

使用操做符 :: 將類(或者對象)與方法名分開。

以下三種狀況使用:

// 1. 對象::實例方法名 (非靜態方法)

class Student {
    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }

    public Integer getAge() { return age; }
}

class Demo {
    public static void main(String[] args) {
        Student student = new Student("jc", 18);

        // 第一種寫法
        Supplier<String> s1 = () -> {
            return student.getName();
        };
        System.out.println(s1.get());

        // 第二種寫法
        Supplier<String> s2 = () -> student.getName();
        System.out.println(s2.get());

        // 方法引用的寫法
        Supplier<String> s3 = student::getName;
        System.out.println(s3.get());
    }
}

只要接口的抽象方法和具體的實現方法,參數同樣、返回值同樣,就能夠這樣用。上面代碼中,student.getName()Supplier 中的 get() 就知足此條件。

// 2. 類::靜態方法名

// 寫法一
Comparator<Integer> c1 = (x, y) -> Integer.compare(x, y);
System.out.println(c1.compare(12, 20));

// 寫法二
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(12, 20));
// 3. 類::實例方法名 (非靜態方法)

// 寫法一
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abc", "abc"));

// 寫法二
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));

這個就有點奇怪了,顯然 equals() 的參數列表,與下方 test() 的明顯不一樣。與上面的說明並不符啊。

其實 (x, y) -> ... 中第一個參數表示:調用方法的調用者

第二個參數就是調用方法的實際傳入參數。只要是這種第一個參數用來調用方法的,第二個參數是傳入參數的,就能使用 類::實例方法名 的方式




構造器引用

利用供給型接口,返回一個對象

class Student {
    String name;

    public Student() { }

    public Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Demo {
    public static void main(String[] args) {
        // 第一種寫法
        Supplier supp = () -> new Student();
        Object stuent = supp.get();
        System.out.println(stuent);
        
        // 第二種寫法
        Supplier supp = Student::new;
        Object stuent = supp.get();
        System.out.println(stuent);
        
        // 試一個能夠傳參的 能夠本身看下 Function 註釋
        Function<String, Student> f = Student::new;
        Object o = f.apply("zhouxuan");
        System.out.println(o);
    }
}

不過須要注意的是,實現的方法的參數,和接口的方法的參數必須一至。爲啥上面調用的是空構造器,由於 get() 沒有參數,那構造函數也調用的是無參的。

// 最後再看個建立數組的例子

// Lambda 表達式
Function<Integer, String[]> f1 = (x) -> new String[x];
String[] r1 = f1.apply(8);
System.out.println(r1.length);

// 數組引用
Function<Integer, String[]> f2 = String[]::new;
String[] r2 = f2.apply(5);
System.out.println(r2.length);




StreamAPI

除了 Lambda,JDK8 另外一個重要改變就是 StreamAPI 了。

StreamAPI 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做。能夠進行復雜的查找、過濾、映射等操做。使用 StreamAPI 對集合數據,在 java 層面進行操做,就相似於使用 SQL 執行的數據庫查詢。StreamAPI 提供了一種高效且易於使用的數據處理方式。

在實際開發中,不少數據來自緩存、數據庫。咱們就可使用 StreamAPI 在 Java 層面高效簡單地處理這些數據。

之前學習的集合,他們的增刪改查,都是數據結構、內存層面的,即數據源是隨之改變的。

如今的 Stream 操做,是 CPU 層面的,數據源不會隨之改變。

使用的步驟:

  1. 先產生一個流 Stream (一個數據源獲取一個流)
  2. 中間鏈式操做 (對數據源的數據進行處理)
  3. 產一個新流 (進行終止操做,此時去執行上一步的中間操做鏈,那就至關因而晚一步延遲執行)




建立 Stream 的幾種方式:

// 方式1:Collection 接口的方法
        Collection<String> col = new ArrayList<>();
        // 獲取串行流
        Stream<String> s1 = col.stream();
        // 獲取並行流
        Stream<String> s2 = col.parallelStream();


        // 方式2:Arrays 中的 Stream 方法
        IntStream s3 = Arrays.stream(new int[]{1, 2, 4});


        // 方式3:Stream 中的 of 方法
        Stream<String> s4 = Stream.of("aa", "cc", "dd");


        // 方式4:建立無限流 (就是每次 +2 無限加, 很神奇 執行一下就知道了)
        Stream<Integer> s5 = Stream.iterate(2, (x) -> x + 2);
        s5.forEach(System.out::println);

        // 也是建立無限流,產生無限多個
        Stream<Double> s6 = Stream.generate(() -> Math.random());
        s6.forEach(System.out::println);




StreamAPI 的中間操做

// 這是數據源 下面的都是操做這兒的        
    List<Student> list = Arrays.asList(
                new Student("lili", 19),
                new Student("ffff", 20),
                new Student("lll", 21),
                new Student("tttt", 22),
                new Student("aaaa", 23),
                new Student("cccc", 24)
        );
// filter 過濾

        // 之前這麼寫
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student item = it.next();
            if (item.getAge() > 21) {
                System.out.println(item);
            }
        }


        // 這是如今
        // 1. 建立 Stream
        Stream<Student> s = list.stream();

        // 2. 中間操做
        Stream<Student> s1 = s.filter((x) -> x.getAge() > 21);

        // 3. 終止操做
        s1.forEach(System.out::println);


        // 再體驗下鏈式操做
        list.stream()
            .filter((x) -> x.getAge() > 21)
            .forEach(System.out::println);
// limit, 截斷(短路)  
        list.stream()
                .filter((x) -> {
                    System.out.println("正在進行 " + x.getName());
                    return x.getAge() > 21;
                })
                // 只要找到兩個 知足了條件 後面就無論了
                .limit(2)
                .forEach(System.out::println);
// skip, 這個和 limit 相反        
        list.stream()
                .filter((x) -> {
                    System.out.println("正在進行 " + x.getName());
                    return x.getAge() > 21;
                })
                // 跳過前兩個知足條件的
                .skip(2)
                .forEach(System.out::println);
// distinct, 去重, 可是須要重寫 hashCode() 和 equals()         
        list.stream()
                .distinct()
                .forEach(System.out::println);
// map, 映射   把原先的 Student 都映射成單個的名字     
        list.stream()
                // .map((x) -> x.getName())
                .map(Student::getName)
                .forEach(System.out::println);
// 例子: 輸出 age > 21 的名字
        list.stream()
                .map((x) -> {
                    if (x.getAge() > 21) {
                        return x.getName();
                    }
                    return null;
                })
                .filter((x) -> x != null)
                .forEach(System.out::println);
// sorted 排序, 這裏暫時新建一個 list
        List<Integer> list = Arrays.asList(1, 3, 6, 2, 8, 11);

        list.stream()
                .sorted()
                .forEach(System.out::println);
// 例子: 按照學生年齡排序
        list.stream()
                .sorted((x, y) -> x.getAge() - y.getAge())
                .forEach(System.out::println);




StreamAPI 的終止操做

// allMatch, 是否是全部 Student 的 age 都知足條件
        boolean result = list.stream()
                .allMatch((x) -> x.getAge() > 20);
        System.out.println(result);
// anyMatch, 有一個知足條件就返回 true
        boolean result = list.stream()
                .anyMatch((x) -> x.getAge() > 21);
        System.out.println(result);
// noneMatch, 都不知足條件就返回 true        
        boolean result = list.stream()
                .noneMatch((x) -> x.getAge() > 100);
        System.out.println(result);
// 還有一些關於查找和匹配的
        findFirst() 返回第一個
        findAny() 返回任意一個    
        count() 獲取此流的元素數
        max() 獲取此流中的最大元素,須要傳入 Comparator
        min() 獲取此流中的最小元素,須要傳入 Comparator
        forEach() 對此流的每一個元素執行操做


接着是規約操做,下面的代碼的意思是:reduce(0,.. 這裏的 0 做爲初始值,初始值賦值給 (x, y) ... 中的 x, y 是每次遍歷獲得的 age,這樣每次將 y 加進 x 中,就能獲得年齡的總和了。最後使用方法引用,語法更簡便。

Integer sumAge = list.stream()
                .map(Student::getAge)
                // .reduce(0, (x, y) -> x + y);
                .reduce(0, Integer::sum);
        System.out.println(sumAge);


最後一個終止操做,收集操做。用於給 Stream 中的元素進行彙總。下面是手機全部 Student 的 name,返回一個 集合。

// List, 簡單的 Demo 獻給你們
        List<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toList());
        System.out.println(allName);

// Set, 固然可使用 Set, 固然 Set 是惟一的哦
        Set<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toSet());
        System.out.println(allName);

// Map, 最後是 Map
        Map<String, Integer> map = list.stream()
                .collect(Collectors.toMap((k) -> k.getName(), (v) -> v.getAge()));
        System.out.println(map);

// count, 獲得學生總數
        Long sum = list.stream()
                .collect(Collectors.counting());
        System.out.println(sum);

// averaging, 獲得平均年齡
        Double d = list.stream()
                .collect(Collectors.averagingDouble((x) -> x.getAge()));
        System.out.println(d);

// sum, 年齡總和
        Double d = list.stream()
                .collect(Collectors.summingDouble((x) -> x.getAge()));
        System.out.println(d);

// max, 獲取年齡最大的, 固然也有 minBy()
        Optional<Student> op = list.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getAge(), y.getAge())));
        System.out.println(op.get());
// 分組, 按年齡分組        
        Map<Integer, List<Student>> map = list.stream()
                .collect(Collectors.groupingBy((x) -> x.getAge()));
        Set<Map.Entry<Integer, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Integer, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }


// 分區, 知足條件的一個區 不知足條件的一個區
        Map<Boolean, List<Student>> map = list.stream()
                .collect(Collectors.partitioningBy((x) -> x.getAge() > 20));
        Set<Map.Entry<Boolean, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Boolean, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }

// 拼接, 將全部的名字進行拼接
        String str = list.stream()
                .map((x) -> x.getName())
                .collect(Collectors.joining());
        System.out.println(str);
相關文章
相關標籤/搜索