JAVA技術
  當前位置:首頁 > 技術支持 > JAVA技術
Java 8 Lambda 表達式
發布時(shí)間:2019-09-28 浏覽:3767次
  

 函數式編程基本概念入門

  •    什(shén)麽是函數式編程 

        函數式編程(英語:functional programming)或稱函數程序設計,又稱泛函編程,是一種編程典範,它将電腦(nǎo)運算(suàn)視爲數學上的(de)函數計算(suàn),并且避免使用(yòng)程序狀态以及易變對(duì)象。函數編程語言最重要的(de)基礎是λ演算(suàn)(lambda calculus)。而且λ演算(suàn)的(de)函數可(kě)以接受函數當作輸入(引數)和(hé)輸出(傳出值)。比起指令式編程,函數式編程更加強調程序執行的(de)結果而非執行的(de)過程,倡導利用(yòng)若幹簡單的(de)執行單元讓計算(suàn)結果不斷漸進,逐層推導複雜(zá)的(de)運算(suàn),而不是設計一個(gè)複雜(zá)的(de)執行過程。這(zhè)是維基百科給出的(de)定義。從這(zhè)個(gè)我們知道函數式編程是相對(duì)于指令式編程的(de)一種編程典範,并且對(duì)而言具有一些優點。

  •   函數式編程的(de)特性與優缺點

      特性

      1、函數是"第一等公民" 

         什(shén)麽是"第一等公民"?所謂"第一等公民"(first class),指的(de)是函數與其他(tā)數據類型一樣,處于平等地位,它不僅擁有一切傳統函數的(de)使用(yòng)方式(聲明(míng)和(hé)調用(yòng)),可(kě)以賦值給其他(tā)變量(賦值),也(yě)可(kě)以作爲參數,傳入另一個(gè)函數(傳參),或者作爲别的(de)函數的(de)返回值(返回)。函數可(kě)以作爲參數進行傳遞,意味我們可(kě)以把行爲"參數化(huà)",處理(lǐ)邏輯可(kě)以從外部傳入,這(zhè)樣程序就可(kě)以設計得(de)更靈活。

     2、沒有"副作用(yòng)"

     所謂"副作用(yòng)"(side effect),指的(de)是函數内部與外部互動(最典型的(de)情況,就是修改全局變量的(de)值),産生運算(suàn)以外的(de)其他(tā)結果。函數式編程強調沒有"副作用(yòng)",意味著(zhe)函數要保持獨立,所有功能就是返回一個(gè)新的(de)值,沒有其他(tā)行爲,尤其是不得(de)修改外部變量的(de)值。

    3、引用(yòng)透明(míng)

    引用(yòng)透明(míng)(Referential transparency),指的(de)是函數的(de)運行不依賴于外部變量或"狀态",隻依賴于輸入的(de)參數,任何時(shí)候隻要參數相同,引用(yòng)函數所得(de)到的(de)返回值總是相同的(de)。這(zhè)裏強調了(le)一點"輸入"不變則"輸出"也(yě)不變,就像數學函數裏面的(de)f(x),隻要輸入的(de)x一樣那得(de)到的(de)結果也(yě)肯定定是一樣的(de)。

      優點:

    1、代碼簡潔,開發快(kuài)速。

     函數式編程大(dà)量使用(yòng)函數,減少了(le)代碼的(de)重複,因此程序比較短,開發速度較快(kuài)。Paul Graham在《黑(hēi)客與畫(huà)家》一書(shū)中寫道:同樣功能的(de)程序,極端情況下(xià),Lisp代碼的(de)長(cháng)度可(kě)能是C代碼的(de)二十分(fēn)之一。如果程序員(yuán)每天所寫的(de)代碼行數基本相同,這(zhè)就意味著(zhe),"C語言需要一年時(shí)間完成開發某個(gè)功能,Lisp語言隻需要不到三星期。反過來(lái)說,如果某個(gè)新功能,Lisp語言完成開發需要三個(gè)月(yuè),C語言需要寫五年。"當然,這(zhè)樣的(de)對(duì)比故意誇大(dà)了(le)差異,但是"在一個(gè)高(gāo)度競争的(de)市場(chǎng)中,即使開發速度隻相差兩三倍,也(yě)足以使得(de)你永遠(yuǎn)處在落後的(de)位置。" 

    2. 接近自然語言,易于理(lǐ)解

       函數式編程的(de)自由度很高(gāo),可(kě)以寫出很接近自然語言的(de)代碼。以java爲例把學生以性别分(fēn)組:

       沒用(yòng)labmda表達式:       

1
2
3
4
5
6
Map<String,List<Student>> studentsMap = new HashMap<>();
        for(Student student : students){
            List<Student> studentList = studentsMap.getOrDefault(student.getSex(), new ArrayList<>());
            studentList.add(student);
            studentsMap.put(student.getSex(),studentList);
        }

  用(yòng)了(le)lambda表達式:

1
Map<String,List<Student>> studentsMap = students.stream().collect(Collectors.groupingBy(Student::getSex));

       這(zhè)基本就是自然語言的(de)表達了(le),大(dà)家應該一眼就能明(míng)白它的(de)意思吧。 

      3. 更方便的(de)代碼管理(lǐ)

      函數式編程不依賴、也(yě)不會改變外界的(de)狀态,隻要給定輸入參數,返回的(de)結果必定相同。因此,每一個(gè)函數都可(kě)以被看做(zuò)獨立單元,很有利于進行單元測試(unit testing)和(hé)除錯(debugging),以及模塊化(huà)組合。 

      4. 易于"并發編程"
      函數式編程不需要考慮"死鎖"(deadlock),因爲它不修改變量,所以根本不存在"鎖"線程的(de)問題。不必擔心一個(gè)線程的(de)數據,被另一個(gè)線程修改,所以可(kě)以很放心地把工作分(fēn)攤到多(duō)個(gè)線程,部署"并發編程"(concurrency)。
     請看下(xià)面的(de)代碼:
     var s1 = Op1();
     var s2 = Op2();
     var s3 = concat(s1, s2);
     由于s1和(hé)s2互不幹擾,不會修改變量,誰先執行是無所謂的(de),所以可(kě)以放心地增加線程,把它們分(fēn)配在兩個(gè)線程上完成。其他(tā)類型的(de)語言就做(zuò)不到這(zhè)一點,因爲s1可(kě)能會修改系統狀态,而s2可(kě)能會用(yòng)到這(zhè)些狀态,所以必須保證s2在s1之後運行,自然也(yě)就不能部署到其他(tā)線程上了(le)。多(duō)核CPU是将來(lái)的(de)潮流,所以函數式編程的(de)這(zhè)個(gè)特性非常重要。

      5. 代碼的(de)熱(rè)升級

      函數式編程沒有副作用(yòng),隻要保證接口不變,内部實現是外部無關的(de)。所以,可(kě)以在運行狀态下(xià)直接升級代碼,不需要重啓,也(yě)不需要停機。Erlang語言早就證明(míng)了(le)這(zhè)一點,它是瑞典愛(ài)立信公司爲了(le)管理(lǐ)電話(huà)系統而開發的(de),電話(huà)系統的(de)升級當然是不能停機的(de)。

     缺點:

     1、函數式編程常被認爲嚴重耗費在CPU和(hé)存儲器資源。主因有二:

  • 早期的(de)函數式編程語言實現時(shí)并無考慮過效率問題。
  • 有些非函數式編程語言爲求提升速度,不提供自動邊界檢查或自動垃圾回收等功能。
    惰性求值亦爲語言如Haskell增加了(le)額外的(de)管理(lǐ)工作。
    
    2、語言學習(xí)曲線陡峭,難度高(gāo)

    函數式語言對(duì)開發者的(de)要求比較高(gāo),學習(xí)曲線比較陡,而且很容易因爲其靈活的(de)語法控制不好程序的(de)結構。

 

     介紹完函數式編程的(de)概念和(hé)優缺點之後,下(xià)面讓我們來(lái)進入java8 lambda的(de)編程世界~

 

      Lambda表達式的(de)組成

       java 8 中Lambda 表達式由三個(gè)部分(fēn)組成:第一部分(fēn)爲一個(gè)括号内用(yòng)逗号分(fēn)隔的(de)形式參數,參數是函數式接口裏面方法的(de)參數;第二部分(fēn)爲一個(gè)箭頭符号:->;第三部分(fēn)爲方法體,可(kě)以是表達式和(hé)代碼塊。語法如下(xià)

       1、方法體爲表達式,該表達式的(de)值作爲返回值返回。

1
2
(parameters) -> expression
(int a,int b) -> return a + b; //求和(hé)

       2、方法體爲代碼塊,必須用(yòng) {} 來(lái)包裹起來(lái),且需要一個(gè) return 返回值,但若函數式接口裏面方法返回值是 void,則無需返回值。

1
2
3
(parameters) -> { statements; }
(int a) -> {System.out.println("a = " + a);} //打印,無返回值
(int a) -> {return a * a;} //求平方

     

      Lambda表達式的(de)底層實現

      java 8 内部Lambda 表達式的(de)實現方式在本質是以匿名内部類的(de)形式的(de)實現的(de),看下(xià)面代碼。代碼中我們定義了(le)一個(gè)叫binaryOperator的(de)Lambda表達式,看返回值它是一個(gè)IntBinaryOperator實例。  

1
2
3
4
5
IntBinaryOperator binaryOperator = (int a, int b) -> {
    return a + b;
};
int result = binaryOperator.applyAsInt(1, 2);
System.out.println("result = " + result); //3

    我們再看一下(xià)IntBinaryOperator的(de)定義 

1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface IntBinaryOperator {
    /**
     * Applies this operator to the given operands.
     * @param left the first operand
     * @param right the second operand
     * @return the operator result
     */
    int applyAsInt(int left, int right);
}

       

     我們得(de)知IntBinaryOperator是一個(gè)接口并且上面有一個(gè)@FunctionalInterface的(de)注解,@FunctionalInterface标注了(le)這(zhè)是一個(gè)函數式接口,所以我們知道了(le)(int a, int b) -> {return a + b;}返回的(de)一個(gè)IntBinaryOperator的(de)匿名實現類。

 

     Lambda表達式的(de)函數式接口 

     上面提到了(le)函數式接口,那這(zhè)是一個(gè)什(shén)麽樣的(de)概念呢(ne)?

      函數式接口(Functional Interface)是Java 8對(duì)一類特殊類型的(de)接口的(de)稱呼。這(zhè)類接口隻定義了(le)唯一的(de)抽象方法的(de)接口(除了(le)隐含的(de)Object對(duì)象的(de)公共方法,因此最開始也(yě)就做(zuò)SAM類型的(de)接口(Single Abstract Method)。定義函數式接口的(de)原因是在Java Lambda的(de)實現中,開發組不想再爲Lambda表達式單獨定義一種特殊的(de)Structural函數類型,稱之爲箭頭類型(arrow type,依然想采用(yòng)Java既有的(de)類型(class, interface, method等).原因是增加一個(gè)結構化(huà)的(de)函數類型會增加函數類型的(de)複雜(zá)性,破壞既有的(de)Java類型,并對(duì)成千上萬的(de)Java類庫造成嚴重的(de)影(yǐng)響。權衡利弊,因此最終還(hái)是利用(yòng)SAM 接口作爲 Lambda表達式的(de)目标類型.另外對(duì)于函數式接口來(lái)說@FunctionalInterface并不是必須的(de),隻要接口中隻定義了(le)唯一的(de)抽象方法的(de)接口那它就是一個(gè)實質上的(de)函數式接口,就可(kě)以用(yòng)來(lái)實現Lambda表達式。

       在java 8中已經爲我們定義了(le)很多(duō)常用(yòng)的(de)函數式接口它們都放在java.util.function包下(xià)面,一般有以下(xià)常用(yòng)的(de)四大(dà)核心接口:       

函數式接口 參數類型 返回類型 用(yòng)途
Consumer<T>(消費型接口) T void 對(duì)類型爲T的(de)對(duì)象應用(yòng)操作。void accept(T t)
Supplier<T>(供給型接口) T 返回類型爲T的(de)對(duì)象。 T get();
Function<T, R>(函數型接口) T R 對(duì)類型爲T的(de)對(duì)象應用(yòng)操作并返回R類型的(de)對(duì)象。R apply(T t);
Predicate<T>(斷言型接口) T boolean 确定類型爲T的(de)對(duì)象是否滿足約束。boolean test(T t);

 

    Lambda表達式的(de)應用(yòng)場(chǎng)景

     1、使用(yòng)() -> {} 替代匿名類

1
2
3
4
5
6
7
8
Thread t1 = new Thread(new Runnable() {
              @Override
              public void run() {
                  System.out.println("no use lambda");
              }
          });
          
Thread t2 = new Thread(() -> System.out.println("use lambda"));

  我們看到相對(duì)而言Lambda表達式要比匿名類要優雅簡潔很多(duō)~。

     2、以流水(shuǐ)線的(de)方式處理(lǐ)數據

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
List<Integer> integers = Arrays.asList(4, 5, 6,1, 2, 3,7, 8,8,9,10);
 
List<Integer> evens = integers.stream().filter(i -> i % 2 == 0)
        .collect(Collectors.toList()); //過濾出偶數列表 [4,6,8,8,10]<br>
List<Integer> sortIntegers = integers.stream().sorted()
        .limit(5).collect(Collectors.toList());//排序并且提取出前5個(gè)元素 [1,2,3,4,5]
 
List<Integer> squareList = integers.stream().map(i -> i * i).collect(Collectors.toList());//轉成平方列表
 
int sum = integers.stream().mapToInt(Integer::intValue).sum();//求和(hé)
 
Set<Integer> integersSet = integers.stream().collect(Collectors.toSet());//轉成其它數據結構比如set
 
Map<Boolean, List<Integer>> listMap = integers.stream().collect(Collectors.groupingBy(i -> i % 2 == 0)); //根據奇偶性分(fēn)組
 
List<Integer> list = integers.stream().filter(i -> i % 2 == 0).map(i -> i * i).distinct().collect(Collectors.toList());//複合操作

  借助stream api和(hé)Lambda表達式,以住需要定義多(duō)個(gè)變量,編寫數十行甚至數百行的(de)代碼的(de)集合操作,現在都基本簡化(huà)成了(le)可(kě)以在一行之内完成~

      3、更簡單的(de)數據并行處理(lǐ)

1
List<Integer> squareList = integers.stream().parallel().map(i -> i * i).collect(Collectors.toList());//轉成平方列表

    數據并行處理(lǐ),隻需要在原來(lái)的(de)基礎上加一個(gè)parallel()就可(kě)以開啓~。順便提一下(xià)這(zhè)裏parallel()開啓的(de)底層并行框架是fork/join,默認的(de)并行數是Ncpu個(gè)。

      4、用(yòng)内部叠代取代外部叠代

       外部叠代:描述怎麽幹,代碼裏嵌套2個(gè)以上的(de)for循環的(de)都比較難讀懂(dǒng);隻能順序處理(lǐ)List中的(de)元素;

       内部叠代:描述要幹什(shén)麽,而不是怎麽幹;不一定需要順序處理(lǐ)List中的(de)元素

1
2
3
4
5
6
7
8
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
    System.out.println(feature); //外部叠代
}
 
List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
 "Date and Time API");
features.stream.forEach(n -> System.out.println(n)); //内部叠代

       5、重構現有臃腫代碼,更高(gāo)的(de)開發效率

      在Lambda表達式出現之前,我們的(de)處理(lǐ)邏輯隻能是以命令式編程的(de)方式來(lái)實現,需要大(dà)量的(de)代碼去編寫程序的(de)每一步操作,定義非常多(duō)的(de)變量,代碼量和(hé)工作量都相對(duì)的(de)巨大(dà)。如果用(yòng)Lambda表達式我們看到以往數十行甚至上百行的(de)代碼都可(kě)以濃縮成幾行甚至一行代碼。這(zhè)樣處理(lǐ)邏輯就會相對(duì)簡單,開發效率可(kě)以得(de)到明(míng)顯提高(gāo),維護工作也(yě)相對(duì)容易。

 

       Lambda表達式中的(de)Stream

        在java 8 中 Stream 不是集合元素,它不保存數據,它是有關算(suàn)法和(hé)計算(suàn)的(de),它更像一個(gè)高(gāo)級版本的(de) Iterator。原始版本的(de) Iterator,用(yòng)戶隻能顯式地一個(gè)一個(gè)遍曆元素并對(duì)其執行某些操作;高(gāo)級版本的(de) Stream,用(yòng)戶隻要給出需要對(duì)其包含的(de)元素執行什(shén)麽操作,比如 “過濾掉長(cháng)度大(dà)于 10 的(de)字符串”、“獲取每個(gè)字符串的(de)首字母”等,Stream 會隐式地在内部進行遍曆,做(zuò)出相應的(de)數據轉換。

        Stream 就如同一個(gè)叠代器(Iterator),單向,不可(kě)往複,數據隻能遍曆一次,遍曆過一次後即用(yòng)盡了(le),就好比流水(shuǐ)從面前流過,一去不複返。而和(hé)叠代器又不同的(de)是,Stream 可(kě)以并行化(huà)操作,叠代器隻能命令式地、串行化(huà)操作。顧名思義,當使用(yòng)串行方式去遍曆時(shí),每個(gè) item 讀完後再讀下(xià)一個(gè) item。而使用(yòng)并行去遍曆時(shí),數據會被分(fēn)成多(duō)個(gè)段,其中每一個(gè)都在不同的(de)線程中處理(lǐ),然後将結果一起輸出。Stream 的(de)并行操作依賴于 Java7 中引入的(de) Fork/Join 框架(JSR166y)來(lái)拆分(fēn)任務和(hé)加速處理(lǐ)過程。

        Stream可(kě)以有限的(de)也(yě)可(kě)以是無限的(de),流的(de)構造方式有很多(duō)可(kě)以從常用(yòng)的(de)Collection(List,Array,Set and so on...),文件,甚至函數....

      由值創建流:

               Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");

       由數組創建流: 

               int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrays.stream(numbers).sum();       

       由文件創建流

               Stream<String> lines =Files.lines(Paths.get("data.txt"), Charset.defaultCharset())

       上面的(de)這(zhè)些Stream都是有限的(de),我們可(kě)以用(yòng)函數來(lái)創建一個(gè)無限Stream

               Stream.iterate(0, n -> n + 2).forEach(System.out::println);

        Stream也(yě)很懶惰,它隻會在你真正需要數據的(de)時(shí)候才會把數據給傳給你,在你不需要時(shí)它一個(gè)數據都不會産生。

 

       Lambda表達式的(de)Best Practice

        1、保持Lambda表達式簡短和(hé)一目了(le)然

1
2
3
4
5
6
7
8
9
10
11
values.stream()
  .mapToInt(e -> {    
    int sum = 0;
    for(int i = 1; i <= e; i++) {
      if(e % i == 0) {
        sum += i;
      }
    }  
    return sum;
  })
  .sum());  //代碼複雜(zá)難懂(dǒng) 
1
2
3
values.stream()
  .mapToInt(e -> sumOfFactors(e))
  .sum() //代碼簡潔一目了(le)然

  長(cháng)長(cháng)的(de)Lambda表達式通(tōng)常是危險的(de),因爲代碼越長(cháng)越難以讀懂(dǒng),意圖看起來(lái)也(yě)不明(míng),并且代碼也(yě)難以複用(yòng),測試難度也(yě)大(dà)。

      2、使用(yòng)@FunctionalInterface 注解

         如果你确定了(le)某個(gè)interface是用(yòng)于Lambda表達式,請一定要加上@FunctionalInterface,表明(míng)你的(de)意圖不然将來(lái)說不定某個(gè)不知情的(de)家夥比如你旁邊的(de)好基友,在這(zhè)個(gè)interface上面加了(le)另外一個(gè)抽像方法時(shí),你的(de)代碼就悲劇了(le)。

      3、優先使用(yòng)java.util.function包下(xià)面的(de)函數式接口

         java.util.function 這(zhè)個(gè)包下(xià)面提供了(le)大(dà)量的(de)功能性接口,可(kě)以滿足大(dà)多(duō)數開發人(rén)員(yuán)爲lambda表達式和(hé)方法引用(yòng)提供目标類型的(de)需求。每個(gè)接口都是通(tōng)用(yòng)的(de)和(hé)抽象的(de),使它們易于适應幾乎任何lambda表達式。開發人(rén)員(yuán)應該在創建新的(de)功能接口之前研究這(zhè)個(gè)包,避免重複定義接口。另外一點就是,裏面的(de)接口不會被别人(rén)修改~。

      4、不要在Lambda表達中執行有"副作用(yòng)"的(de)操作

        "副作用(yòng)"是嚴重違背函數式編程的(de)設計原則,在工作中我經常看到有人(rén)在forEach操作裏面操作外面的(de)某個(gè)List或者設置某個(gè)Map這(zhè)其實是不對(duì)的(de)。

      5、不要把Lambda表達式和(hé)匿名内部類同等對(duì)待

         雖然我們可(kě)以用(yòng)匿名内部類來(lái)實現Lambda表達式,也(yě)可(kě)以用(yòng)Lambda表達式來(lái)替換内部類,但并不代表這(zhè)兩者是等價的(de)。這(zhè)兩者在某一個(gè)重要概念是不同的(de):this指代的(de)上下(xià)文是不一樣的(de)。當您使用(yòng)内部類時(shí),它将創建一個(gè)新的(de)範圍。通(tōng)過實例化(huà)具有相同名稱的(de)新局部變量,可(kě)以從封閉範圍覆蓋局部變量。您還(hái)可(kě)以在内部類中使用(yòng)這(zhè)個(gè)關鍵字作爲它實例的(de)引用(yòng)。但是,lambda表達式可(kě)以使用(yòng)封閉範圍。您不能在lambda的(de)主體内覆蓋範圍内的(de)變量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private String value = "Enclosing scope value";
 
public String scopeExperiment() {
    Foo fooIC = new Foo() {
        String value = "Inner class value";
  
        @Override
        public String method(String string) {
            return this.value;
        }
    };
    String resultIC = fooIC.method("");
  
    Foo fooLambda = parameter -> {
        String value = "Lambda value";
        return this.value;
    };
    String resultLambda = fooLambda.method("");
  
    return "Results: resultIC = " + resultIC +
      ", resultLambda = " + resultLambda;
}

   運行上面這(zhè)段代碼我們将到 resultIC = "Inner class value",resultLambda = "Enclosing scope value"。也(yě)就是說在匿名内部類中this指的(de)是自身的(de)引用(yòng),在Lambda表達式中this指的(de)是外部。

       6、多(duō)使用(yòng)方法引用(yòng)

       在Lambda表達式中 a -> a.toLowerCase()和(hé)String::toLowerCase都能起到相同的(de)作用(yòng),但兩者相比,後者通(tōng)常可(kě)讀性更高(gāo)并且代碼會簡短。

       7、盡量避免在Lambda的(de)方法體中使用(yòng){}代碼塊

       優先使用(yòng)

1
2
3
4
5
6
Foo foo = parameter -> buildString(parameter);
private String buildString(String parameter) {
    String result = "Something " + parameter;
    //many lines of code
    return result;
}

     而不是

1
2
3
4
Foo foo = parameter -> { String result = "Something " + parameter;
    //many lines of code
    return result;
};

  8、不要盲目的(de)開啓并行流

       Lambda的(de)并行流雖好,但也(yě)要注意使用(yòng)場(chǎng)景。如果平常的(de)業務處理(lǐ)比如過濾,提取數據,沒有涉及特别大(dà)的(de)數據和(hé)耗時(shí)操作,則真的(de)不需要開啓并行流。我在工作中看到有些人(rén)一個(gè)隻有幾十個(gè)元素的(de)列表的(de)過濾操作也(yě)開啓了(le)并行流,其實這(zhè)樣做(zuò)會更慢(màn)。因爲多(duō)行線程的(de)開啓和(hé)同步這(zhè)些花費的(de)時(shí)間往往比你真實的(de)處理(lǐ)時(shí)間要多(duō)很多(duō)。但一些耗時(shí)的(de)操作比如I/O訪問,DB查詢,遠(yuǎn)程調用(yòng),這(zhè)些如果可(kě)以并行的(de)話(huà),則開啓并行流是可(kě)提升很大(dà)性能的(de)。因爲并行流的(de)底層原理(lǐ)是fork/join,如果你的(de)數據分(fēn)塊不是很好切分(fēn),也(yě)不建議(yì)開啓并行流。舉個(gè)例子ArrayList的(de)Stream可(kě)以開啓并行流,而LinkedList則不建議(yì),因爲LinkedList每次做(zuò)數據切分(fēn)要遍曆整個(gè)鏈表,這(zhè)本身就已經很浪費性能,而ArrayList則不會。

   

 
 
    
 
 
版權所有 © 2005-2024 安徽問法信息技術有限公司  
地址:安徽省合肥市蜀山區(qū)望江西路69号西湖國際廣場(chǎng) 電話(huà):0551-64935878 郵箱:admin@ajsoft.cn