JAVA8給我帶了什麼——並行流和介面新功能

来源:https://www.cnblogs.com/hayasi/archive/2019/04/01/10639994.html
-Advertisement-
Play Games

流,確定是筆者內心很嚮往的天堂,有他之後JAVA在處理數據就變更加的靈動。加上lambda表達不喜歡都不行。JAVA8也為流在提供另一個功能——並行流。即是有並行流,那麼是不是也有順序流。沒有錯。我前面操作的一般都是順序流。在JAVA8裡面並行流和順序流是可以轉變的。來看一個例子——筆者列印數字。 ...


流,確定是筆者內心很嚮往的天堂,有他之後JAVA在處理數據就變更加的靈動。加上lambda表達不喜歡都不行。JAVA8也為流在提供另一個功能——並行流。即是有並行流,那麼是不是也有順序流。沒有錯。我前面操作的一般都是順序流。在JAVA8裡面並行流和順序流是可以轉變的。來看一個例子——筆者列印數字。

 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 }

運行結果:

倆個結果相比一下,我們就可以明顯他們發生了變化。我們只是加一個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函數就是用來建一個並行流的。運行結果:

轉為順序流

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     }

 運行結果:

我們都知道流裡面用到了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();
    }
}

 當然除了上面之外,你還是可以定義靜態方法和常量。這個時候有人就會說他不是跟抽象類很像嗎?是很像。可是不一樣子。抽象類是不是可以實例一個欄位。但是介面卻不行。還有抽像類你只能單繼承。介面就可以多繼承了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 IGame游戲公司的故事 1.1 討論會 話說有一個叫IGame的游戲公司,正在開發一款ARPG游戲(動作&角色扮演類游戲,如魔獸世界、夢幻西游這一類的游戲)。一般這類游戲都有一個基本的功能,就是打怪(玩家攻擊怪物,藉此獲得經驗、虛擬貨幣和虛擬裝備),並且根據玩家角色所裝備的武器不同,攻擊效果也 ...
  • 定義: 保證一個類僅有一個實例,並提供一個全局訪問點 適用場景: 確保任何情況下這個對象只有一個實例 詳解: 1.私有構造器: 將本類的構造器私有化,其實這是單例的一個非常重要的步驟,沒有這個步驟,可以說你的就不是單例模式。這個步驟其實是防止外部函數在new的時候能構造出來新的對象,我們說單例要保證 ...
  • 又和大家見面了。首先,和大家說聲抱歉,之前的幾篇文章,可能條理清晰之類的做的不太好,每篇文章的篇幅也比較長,小編在收到讀者的建議之後, 也是認真的思考了一番。之前的想法是儘量把一個模塊介紹完,沒想到一個模塊寫著寫著就寫長了。在之後的文章里,需要認真分段,做到能簡潔就簡潔,能分塊就分塊,在利用大家碎片 ...
  • 官網:www.fhadmin.org 工作流模塊 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4. ...
  • 記錄一下最近整理的spring boot項目 項目地址:https://gitee.com/xl0917/spring-boot 1.選擇Spring Initializr 一直點擊next,直到創建完成 2.創建spring boot子模塊,創建無任何模板的maven項目 3.項目結構 4.pom ...
  • 前言 去年年底,博主有購房的意願,本來是打算在青島市北購房,怎奈工作變動,意向轉移到了李滄,坐等了半年以後,最終選擇在紅島附近購置了期房。 也許一些知道青島紅島的小伙伴會問我,為什麼會跑到那鳥不拉屎的地方去買房子,目前只能是一個字:"賭、賭、賭",重要的事情說三遍。下麵來分析一下,我為什麼沒有在李滄 ...
  • 驗證碼探究 如果你是一個數據挖掘愛好者,那麼驗證碼是你避免不過去的一個天坑,和各種驗證碼鬥爭,必然是你成長的一條道路,接下來的幾篇文章,我會儘量的找到各種驗證碼,並且去嘗試解決掉它,中間有些技術甚至我都沒有見過,來吧,一起Coding吧 數字+字母的驗證碼 我隨便在百度圖片搜索了一個驗證碼,如下 今 ...
  • 1.別瞎寫,方法里能用封裝好的類,就別自己寫HashMap. 2.方法名,整的方法名都是啥?退出close,用out. 3.git提交版本,自己寫的代碼,註釋,提交版本的時候,一定要清理掉.每個判斷能不能用單元測試測測. 4.啥時候捕獲錯誤,什麼時候拋出錯誤,什麼時候聲明錯誤,在方法里每個都try_ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...