如今 Java 按照週期發佈,每六個月就更新一次。所謂 「小步快跑,快速迭代」java
JDK8 是 Java 語言開發的一個主要版本,2014年 3月發佈,是 JDK5 以來最具革命性的版本。數據庫
新特性簡介:api
速度更快 (HashMap 的數據結構更改)數組
代碼更少 (Lambda 表達式)緩存
Stream API數據結構
便於並行app
減小控制針異常 (Optional)dom
Nashorn引擎,容許在 JVM 上運行 JS 應用 (使用 jjs.exe JDK 11中沒有了)ide
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
,這個包裏面有幾個經常使用的函數式接口:
這些接口功能就是,不用你再去建立新接口,按照名字的語義。直接用它的就好了,好比:
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);
除了 Lambda,JDK8 另外一個重要改變就是 StreamAPI 了。
StreamAPI 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做。能夠進行復雜的查找、過濾、映射等操做。使用 StreamAPI 對集合數據,在 java 層面進行操做,就相似於使用 SQL 執行的數據庫查詢。StreamAPI 提供了一種高效且易於使用的數據處理方式。
在實際開發中,不少數據來自緩存、數據庫。咱們就可使用 StreamAPI 在 Java 層面高效簡單地處理這些數據。
之前學習的集合,他們的增刪改查,都是數據結構、內存層面的,即數據源是隨之改變的。
如今的 Stream 操做,是 CPU 層面的,數據源不會隨之改變。
使用的步驟:
// 方式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);
// 這是數據源 下面的都是操做這兒的 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);
// 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);