JAVA8給我帶了什麼——並行流和接口新功能

流,肯定是筆者心裏很嚮往的天堂,有他以後JAVA在處理數據就變動加的靈動。加上lambda表達不喜歡都不行。JAVA8也爲流在提供另外一個功能——並行流。便是有並行流,那麼是否是也有順序流。沒有錯。我前面操做的通常都是順序流。在JAVA8裏面並行流和順序流是能夠轉變的。來看一個例子——筆者打印數字。java

 1 package com.aomi;
 2 
 3 import java.util.stream.LongStream;
 4 
 5 public class Main {
 6 
 7     public static void main(String[] args) {
 8         // TODO Auto-generated method stub
 9 
10         LongStream.range(0, 10).forEach(i -> {
11             System.out.print(i + " ");
12         });
13     }
14 
15 }

LongStream.range這個方法是來獲取數字的。這裏表示得到0到10,但不含10 的數字。運行結果:編程

如今讓咱們把他換成並行來看看。框架

 1 package com.aomi;
 2 
 3 import java.util.stream.LongStream;
 4 
 5 public class Main {
 6 
 7     public static void main(String[] args) {
 8         // TODO Auto-generated method stub
 9 
10         LongStream.range(0, 10).parallel().forEach(i -> {
11             System.out.print(i + " ");
12         });
13     }
14 
15 }

運行結果:ide

倆個結果相比一下,咱們就能夠明顯他們發生了變化。咱們只是加一個parallel函數就發生好多的變化。筆者原本是要講他們之間的性能比較的。不敢,由於筆者試好還有個例子。卻發現有時候順序流都比並行流來快。上面是順序流轉並行流。在來看一下相反的。函數

1 public static void main(String[] args) {
2         // TODO Auto-generated method stub
3 
4         List<Integer> datas = Arrays.asList(1,2,3,4,56);
5         
6         datas.parallelStream().forEach(i -> {
7             System.out.print(i + " ");
8         });
9     }

parallelStream函數就是用來建一個並行流的。運行結果:性能

轉爲順序流this

1 public static void main(String[] args) {
2         // TODO Auto-generated method stub
3 
4         List<Integer> datas = Arrays.asList(1,2,3,4,56);
5         
6         datas.parallelStream().sequential().forEach(i -> {
7             System.out.print(i + " ");
8         });
9     }

 運行結果:spa

咱們都知道流裏面用到了JAVA7裏面的分支和合並的框架來進行的。古代有一個詞叫分而治之。把一個事情分爲幾個小事件。然面各自處理。因此瞭解代碼裏面是什麼樣子折分紅小事件是很是重要的。他有倆個關鍵字Fork和Join。Fork方法你能夠理解爲拆分,並壓入線程隊列中。而Join就是合併的意思了。來筆者來寫一個試。線程

 1 package com.aomi;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.concurrent.RecursiveTask;
 6 
 7 public class DistinctCharForkJoin extends RecursiveTask<List<Character>> {
 8 
 9     private List<Character> chars;
10 
11     public DistinctCharForkJoin(List<Character> chars) {
12         this(chars, 0, chars.size());
13     }
14 
15     public DistinctCharForkJoin(List<Character> chars, int start, int end) {
16 
17         this.chars = chars.subList(start, end);
18     }
19 
20     @Override
21     protected List<Character> compute() {
22         // TODO Auto-generated method stub
23         List<Character> tmpChars = new ArrayList<Character>();
24 
25         // 判斷不能夠在拆分了
26         if (this.chars.size() < 3) {
27 
28             for (Character character : chars) {
29                 if (!tmpChars.contains(character))
30                     tmpChars.add(character);
31             }
32 
33         } else {// 表示能夠在拆分。
34 
35             int len = this.chars.size();
36 
37             // 創建左邊的小事件
38             DistinctCharForkJoin leftForkJoin = new DistinctCharForkJoin(chars, 0, len / 2);
39 
40             leftForkJoin.fork();
41 
42             // 創建右邊的小事件
43             DistinctCharForkJoin rightForkJoin = new DistinctCharForkJoin(chars, len / 2, len);
44 
45             rightForkJoin.fork();
46 
47             List<Character> rChars = rightForkJoin.join();
48 
49             List<Character> lChars = leftForkJoin.join();
50 
51             // 倆個合併。
52             for (Character character : rChars) {
53                 if (!tmpChars.contains(character))
54                     tmpChars.add(character);
55             }
56 
57             for (Character character : lChars) {
58                 if (!tmpChars.contains(character))
59                     tmpChars.add(character);
60             }
61 
62         }
63 
64         return tmpChars;
65     }
66 
67 }

Main:設計

 1 public static void main(String[] args) {
 2         // TODO Auto-generated method stub
 3 
 4         List<Character> chars = Arrays.asList('a', 'b', 'c', 'd', 'b', 'a');
 5 
 6         DistinctCharForkJoin task = new DistinctCharForkJoin("main", chars);
 7 
 8         List<Character> resChars = new ForkJoinPool().invoke(task);
 9 
10         for (Character character : resChars) {
11 
12             System.out.print(character +" ");
13         }
14     }

運行結果:

大家必定很奇怪爲何筆者會講到JAVA7帶來的東西呢?JAVA8引入了一個新的接口——Spliterator接口。人稱可分迭代器。若是你有心去看一個接口List的話,你可能會發現一個方法。以下

1 default Spliterator<E> spliterator() {
2         return Spliterators.spliterator(this, Spliterator.ORDERED);
3     }

Spliterator接口:

1 public interface Spliterator<T> {
2     boolean tryAdvance(Consumer<? super T> action);
3     Spliterator<T> trySplit();
4     long estimateSize();
5     int characteristics();
6 }

講JAVA7裏面的分支/合併的目地就是爲了理解Spliterator接口的做用。以下

  • tryAdvance:用於遍歷當前的元素。若是還有的話,就返回true;
  • trySplit:用於拆分。若是當前不能夠在拆分的話,就返回null;跟上面的compute方法很像。
  • estimateSize:表示還須要遍歷的元素有多少。
  • characteristics:表示當前處理的數據是什麼樣子的。好比是否有序,每一元素是否爲null。上面Spliterator接口的代碼是筆者去掉大部分複製出來。這個值都在代碼中。做用大家能夠本身去看一下代碼就是知道。

要注意Spliterator接口只是用去拆分任務的做用。JAVA8幫你作了不少拆分的功能。大部分你能夠不用本身寫。固然若是你想要本身動手。你只要實現這樣子就能夠了。以下

 1 package com.aomi;
 2 
 3 import java.util.List;
 4 import java.util.Spliterator;
 5 import java.util.function.Consumer;
 6 
 7 public class DistinctCharSpliterator implements Spliterator<Character> {
 8 
 9     private List<Character> chars;
10     private int index = 0;
11 
12     public DistinctCharSpliterator(List<Character> chars) {
13         this.chars = chars;
14     }
15 
16     public DistinctCharSpliterator(List<Character> chars, int start, int end) {
17         this.chars = chars.subList(start, end);
18     }
19 
20     @Override
21     public boolean tryAdvance(Consumer<? super Character> action) {
22         // TODO Auto-generated method stub
23         action.accept(this.chars.get(index++));
24         return index < this.chars.size();
25     }
26 
27     @Override
28     public Spliterator<Character> trySplit() {
29         // TODO Auto-generated method stub
30         int difLen = this.chars.size() - index;
31 
32         // 判斷不能夠在拆分了
33         if (difLen < 3) {
34             return null;
35         } else {// 表示能夠在拆分。
36 
37             
38             DistinctCharSpliterator spliterator = new DistinctCharSpliterator(chars.subList(index, index + 2));
39 
40             index = index + 2;
41 
42             return spliterator;
43 
44         }
45     }
46 
47     @Override
48     public long estimateSize() {
49         // TODO Auto-generated method stub
50         return this.chars.size() - index;
51     }
52 
53     @Override
54     public int characteristics() {
55         // TODO Auto-generated method stub
56         // 有序 元素不空 遍歷過程不能刪除,和修改 增長
57         return ORDERED + NONNULL + IMMUTABLE;
58     }
59 
60 }

Main:

 1 public static void main(String[] args) {
 2         // TODO Auto-generated method stub
 3 
 4         List<Character> chars = Arrays.asList('a', 'b', 'c', 'd', 'b', 'a');
 5 
 6         DistinctCharSpliterator distinctCharSpliterator = new DistinctCharSpliterator(chars);
 7 
 8         Stream<Character> stream = StreamSupport.stream(distinctCharSpliterator, true);
 9 
10         stream.distinct().forEach((Character ch) -> {
11 
12             System.out.print(ch+" ");
13         });
14 
15     }

運行結果:

上面的例子有一點爛。可是你們能夠複製作一下繼點去看看他的執行過程。就能夠看出不少東西來。主要是理解這個原理就能夠了。
流的並行功能並無讓筆者有多心動。真正讓筆者感受不錯的要屬於JAVA8對接口的升級。什麼意思?筆者不清楚有多少我的寫個框架或是讀過框架源碼,通常框架裏面都會用到一些面向接口的編程模式。那個或多或少會有這樣子感受。一但項目發佈出去,這個時候你想要修改接口。好比在接口裏面增長一個新的功能方法。這樣子時候你就不得不考慮一下外面有多少我的在實現你如今框架的接口。由於你增長一個接口的新方法。別人也要跟着實現,否則的必定會報錯或是運行時候報錯。無論哪種都是設計者不想看到的。
JAVA8如今可讓你定義接口的默認方法。什麼思意呢?讓筆得寫一個例子。

Base接口:

1 package com.aomi;
2 
3 public interface Base {
4     void call();
5 }

BaseA類:

 1 package com.aomi;
 2 
 3 public class BaseA implements Base {
 4 
 5     @Override
 6     public void call() {
 7         
 8     }
 9 
10 }

Main:

 1 package com.aomi;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7 
 8         Base baseA = new BaseA();
 9 
10         baseA.call();
11     }
12 }

上面的代碼沒有什麼特別的。如今筆者在加一個方法。看一個他會不會有問題。以下

base類:

1 package com.aomi;
2 
3 public interface Base {
4     void call();
5     void call2();
6 }

結果:

看到吧。BaseA類立刻就報錯。如今筆者在加上一個默認的方法會什麼呢?

package com.aomi;

public interface Base {
    void call();

    default void call2() {
        System.out.println("default call2");
    }
}

Main修改一下吧。

 1 package com.aomi;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7 
 8         Base baseA = new BaseA();
 9 
10         baseA.call2();
11     }
12 }

運行結果:

上面的代碼。筆者在BaseA類裏面並無實現call2的方法。顯然如今的功能對咱們寫框架的人來寫太棒了。在也不用擔憂增長一個接方法而去考慮有多少我的用這個接口了。
那麼問題來了。咱們在寫代碼的過程當中,必定會遇到方法相同的狀況吧。這個時候JAVA8提供了三個標準來肯定用哪個。

  1. 類或父類的方法優先級高於接口默認的方法。
  2. 若是上面不行的話,誰擁有最具體的實現的話,就用誰。
  3. 若是都不能肯定的狀況下,就必須顯性的調用。來指定他要調哪個。

舉例子。A和B都是接口。其中B繼承了A。同時C實現了A和B。這個時候調用C會是什麼樣子。
A:

public interface A {

    default void call() {
        System.out.println("A call");
    }
}

B:

public interface B  extends A {
    default void call() {
        System.out.println("B call");
    }
}

C:

public class C implements A, B {

}

D:

public static void main(String[] args) {
        // TODO Auto-generated method stub

        C c = new C();
        
        c.call();

    }

運行結果:

上面A和B都是接口。他們有call方法。其中關鍵是B繼承了。說明B擁有A的一切方法。那麼是否是說B就是最具體實現的。若是大家只用第一個標準的話,那是確定不行的。
仍是簡單一點,咱們把B繼承A的這個關係去掉,在來看看。

很差意思好像報錯了。因此只能苦一下了。顯性調用。

package com.aomi;

public class C implements B, A {

    public void call() {
        B.super.call();
    }
}

 固然除了上面以外,你仍是能夠定義靜態方法和常量。這個時候有人就會說他不是跟抽象類很像嗎?是很像。但是不同子。抽象類是否是能夠實例一個字段。可是接口卻不行。還有抽像類你只能單繼承。接口就能夠多繼承了。

相關文章
相關標籤/搜索