精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java中的數(shù)據(jù)流和函數(shù)式編程

開發(fā) 后端
當 Java SE 8(又名核心 Java 8)在 2014 年被推出時,它引入了一些更改,從根本上影響了用它進行的編程。這些更改中有兩個緊密相連的部分:流 API 和函數(shù)式編程構造。本文使用代碼示例,從基礎到高級特性,介紹每個部分并說明它們之間的相互作用。

[[314177]]

學習如何使用 Java 8 中的流 API 和函數(shù)式編程結構。

當 Java SE 8(又名核心 Java 8)在 2014 年被推出時,它引入了一些更改,從根本上影響了用它進行的編程。這些更改中有兩個緊密相連的部分:流 API 和函數(shù)式編程構造。本文使用代碼示例,從基礎到高級特性,介紹每個部分并說明它們之間的相互作用。

基礎特性

流 API 是在數(shù)據(jù)序列中迭代元素的簡潔而高級的方法。包 java.util.streamjava.util.function 包含了用于流 API 和相關函數(shù)式編程構造的新庫。當然,代碼示例勝過千言萬語。

下面的代碼段用大約 2,000 個隨機整數(shù)值填充了一個 List

  1. Random rand = new Random2();
  2. List<Integer> list = new ArrayList<Integer>(); // 空 list
  3. for (int i = 0; i < 2048; i++) list.add(rand.nextInt()); // 填充它

另外用一個 for 循環(huán)可用于遍歷填充列表,以將偶數(shù)值收集到另一個列表中。

流 API 提供了一種更簡潔的方法來執(zhí)行此操作:

  1. List <Integer> evens = list
  2. .stream() // 流化 list
  3. .filter(n -> (n & 0x1) == 0) // 過濾出奇數(shù)值
  4. .collect(Collectors.toList()); // 收集偶數(shù)值

這個例子有三個來自流 API 的函數(shù):

  • stream 函數(shù)可以將集合轉換為流,而流是一個每次可訪問一個值的傳送帶。流化是惰性的(因此也是高效的),因為值是根據(jù)需要產生的,而不是一次性產生的。
  • filter 函數(shù)確定哪些流的值(如果有的話)通過了處理管道中的下一個階段,即 collect 階段。filter 函數(shù)是 高階的higher-order,因為它的參數(shù)是一個函數(shù) —— 在這個例子中是一個 lambda 表達式,它是一個未命名的函數(shù),并且是 Java 新的函數(shù)式編程結構的核心。

lambda 語法與傳統(tǒng)的 Java 完全不同:

  1. n -> (n & 0x1) == 0

箭頭(一個減號后面緊跟著一個大于號)將左邊的參數(shù)列表與右邊的函數(shù)體分隔開。參數(shù) n 雖未明確類型,但也可以明確。在任何情況下,編譯器都會發(fā)現(xiàn) n 是個 Integer。如果有多個參數(shù),這些參數(shù)將被括在括號中,并用逗號分隔。

在本例中,函數(shù)體檢查一個整數(shù)的最低位(最右)是否為零,這用來表示偶數(shù)。過濾器應返回一個布爾值。盡管可以,但該函數(shù)的主體中沒有顯式的 return。如果主體沒有顯式的 return,則主體的最后一個表達式即是返回值。在這個例子中,主體按照 lambda 編程的思想編寫,由一個簡單的布爾表達式 (n & 0x1) == 0 組成。

  • collect 函數(shù)將偶數(shù)值收集到引用為 evens 的列表中。如下例所示,collect 函數(shù)是線程安全的,因此,即使在多個線程之間共享了過濾操作,該函數(shù)也可以正常工作。

方便的功能和輕松實現(xiàn)多線程

在生產環(huán)境中,數(shù)據(jù)流的源可能是文件或網絡連接。為了學習流 API, Java 提供了諸如 IntStream 這樣的類型,它可以用各種類型的元素生成流。這里有一個 IntStream 的例子:

  1. IntStream // 整型流
  2. .range(1, 2048) // 生成此范圍內的整型流
  3. .parallel() // 為多個線程分區(qū)數(shù)據(jù)
  4. .filter(i -> ((i & 0x1) > 0)) // 奇偶校驗 - 只允許奇數(shù)通過
  5. .forEach(System.out::println); // 打印每個值

IntStream 類型包括一個 range 函數(shù),該函數(shù)在指定的范圍內生成一個整數(shù)值流,在本例中,以 1 為增量,從 1 遞增到 2048。parallel 函數(shù)自動劃分該工作到多個線程中,在各個線程中進行過濾和打印。(線程數(shù)通常與主機系統(tǒng)上的 CPU 數(shù)量匹配。)函數(shù) forEach 參數(shù)是一個方法引用,在本例中是對封裝在 System.out 中的 println 方法的引用,方法輸出類型為 PrintStream。方法和構造器引用的語法將在稍后討論。

由于具有多線程,因此整數(shù)值整體上以任意順序打印,但在給定線程中是按順序打印的。例如,如果線程 T1 打印 409 和 411,那么 T1 將按照順序 409-411 打印,但是其它某個線程可能會預先打印 2045。parallel 調用后面的線程是并發(fā)執(zhí)行的,因此它們的輸出順序是不確定的。

map/reduce 模式

map/reduce 模式在處理大型數(shù)據(jù)集方面變得很流行。一個 map/reduce 宏操作由兩個微操作構成。首先,將數(shù)據(jù)分散(映射mapped)到各個工作程序中,然后將單獨的結果收集在一起 —— 也可能收集統(tǒng)計起來成為一個值,即歸約reduction。歸約可以采用不同的形式,如以下示例所示。

下面 Number 類的實例用 EVENODD 表示有奇偶校驗的整數(shù)值:

  1. public class Number {
  2. enum Parity { EVEN, ODD }
  3. private int value;
  4. public Number(int n) { setValue(n); }
  5. public void setValue(int value) { this.value = value; }
  6. public int getValue() { return this.value; }
  7. public Parity getParity() {
  8. return ((value & 0x1) == 0) ? Parity.EVEN : Parity.ODD;
  9. }
  10. public void dump() {
  11. System.out.format("Value: %2d (parity: %s)\n", getValue(),
  12. (getParity() == Parity.ODD ? "odd" : "even"));
  13. }
  14. }

下面的代碼演示了用 Number 流進行 map/reduce 的情形,從而表明流 API 不僅可以處理 intfloat 等基本類型,還可以處理程序員自定義的類類型。

在下面的代碼段中,使用了 parallelStream 而不是 stream 函數(shù)對隨機整數(shù)值列表進行流化處理。與前面介紹的 parallel 函數(shù)一樣,parallelStream 變體也可以自動執(zhí)行多線程。

  1. final int howMany = 200;
  2. Random r = new Random();
  3. Number[] nums = new Number[howMany];
  4. for (int i = 0; i < howMany; i++) nums[i] = new Number(r.nextInt(100));
  5. List<Number> listOfNums = Arrays.asList(nums); // 將數(shù)組轉化為 list
  6.  
  7. Integer sum4All = listOfNums
  8. .parallelStream() // 自動執(zhí)行多線程
  9. .mapToInt(Number::getValue) // 使用方法引用,而不是 lambda
  10. .sum(); // 將流值計算出和值
  11. System.out.println("The sum of the randomly generated values is: " + sum4All);

高階的 mapToInt 函數(shù)可以接受一個 lambda 作為參數(shù),但在本例中,它接受一個方法引用,即 Number::getValuegetValue 方法不需要參數(shù),它返回給定的 Number 實例的 int 值。語法并不復雜:類名 Number 后跟一個雙冒號和方法名。回想一下先前的例子 System.out::println,它在 System 類中的 static 屬性 out 后面有一個雙冒號。

方法引用 Number::getValue 可以用下面的 lambda 表達式替換。參數(shù) n 是流中的 Number 實例中的之一:

  1. mapToInt(n -> n.getValue())

通常,lambda 表達式和方法引用是可互換的:如果像 mapToInt 這樣的高階函數(shù)可以采用一種形式作為參數(shù),那么這個函數(shù)也可以采用另一種形式。這兩個函數(shù)式編程結構具有相同的目的 —— 對作為參數(shù)傳入的數(shù)據(jù)執(zhí)行一些自定義操作。在兩者之間進行選擇通常是為了方便。例如,lambda 可以在沒有封裝類的情況下編寫,而方法則不能。我的習慣是使用 lambda,除非已經有了適當?shù)姆庋b方法。

當前示例末尾的 sum 函數(shù)通過結合來自 parallelStream 線程的部分和,以線程安全的方式進行歸約。但是,程序員有責任確保在 parallelStream 調用引發(fā)的多線程過程中,程序員自己的函數(shù)調用(在本例中為 getValue)是線程安全的。

最后一點值得強調。lambda 語法鼓勵編寫純函數(shù)pure function,即函數(shù)的返回值僅取決于傳入的參數(shù)(如果有);純函數(shù)沒有副作用,例如更新一個類中的 static 字段。因此,純函數(shù)是線程安全的,并且如果傳遞給高階函數(shù)的函數(shù)參數(shù)(例如 filtermap )是純函數(shù),則流 API 效果最佳。

對于更細粒度的控制,有另一個流 API 函數(shù),名為 reduce,可用于對 Number 流中的值求和:

  1. Integer sum4AllHarder = listOfNums
  2. .parallelStream() // 多線程
  3. .map(Number::getValue) // 每個 Number 的值
  4. .reduce(0, (sofar, next) -> sofar + next); // 求和

此版本的 reduce 函數(shù)帶有兩個參數(shù),第二個參數(shù)是一個函數(shù):

  • 第一個參數(shù)(在這種情況下為零)是特征值,該值用作求和操作的初始值,并且在求和過程中流結束時用作默認值。
  • 第二個參數(shù)是累加器,在本例中,這個 lambda 表達式有兩個參數(shù):第一個參數(shù)(sofar)是正在運行的和,第二個參數(shù)(next)是來自流的下一個值。運行的和以及下一個值相加,然后更新累加器。請記住,由于開始時調用了 parallelStream,因此 mapreduce 函數(shù)現(xiàn)在都在多線程上下文中執(zhí)行。

在到目前為止的示例中,流值被收集,然后被規(guī)約,但是,通常情況下,流 API 中的 Collectors 可以累積值,而不需要將它們規(guī)約到單個值。正如下一個代碼段所示,收集活動可以生成任意豐富的數(shù)據(jù)結構。該示例使用與前面示例相同的 listOfNums

  1. Map<Number.Parity, List<Number>> numMap = listOfNums
  2. .parallelStream()
  3. .collect(Collectors.groupingBy(Number::getParity));
  4.  
  5. List<Number> evens = numMap.get(Number.Parity.EVEN);
  6. List<Number> odds = numMap.get(Number.Parity.ODD);

第一行中的 numMap 指的是一個 Map,它的鍵是一個 Number 奇偶校驗位(ODDEVEN),其值是一個具有指定奇偶校驗位值的 Number 實例的 List。同樣,通過 parallelStream 調用進行多線程處理,然后 collect 調用(以線程安全的方式)將部分結果組裝到 numMap 引用的 Map 中。然后,在 numMap 上調用 get 方法兩次,一次獲取 evens,第二次獲取 odds

實用函數(shù) dumpList 再次使用來自流 API 的高階 forEach 函數(shù):

  1. private void dumpList(String msg, List<Number> list) {
  2. System.out.println("\n" + msg);
  3. list.stream().forEach(n -> n.dump()); // 或者使用 forEach(Number::dump)
  4. }

這是示例運行中程序輸出的一部分:

  1. The sum of the randomly generated values is: 3322
  2. The sum again, using a different method: 3322
  3.  
  4. Evens:
  5.  
  6. Value: 72 (parity: even)
  7. Value: 54 (parity: even)
  8. ...
  9. Value: 92 (parity: even)
  10.  
  11. Odds:
  12.  
  13. Value: 35 (parity: odd)
  14. Value: 37 (parity: odd)
  15. ...
  16. Value: 41 (parity: odd)

用于代碼簡化的函數(shù)式結構

函數(shù)式結構(如方法引用和 lambda 表達式)非常適合在流 API 中使用。這些構造代表了 Java 中對高階函數(shù)的主要簡化。即使在糟糕的過去,Java 也通過 MethodConstructor 類型在技術上支持高階函數(shù),這些類型的實例可以作為參數(shù)傳遞給其它函數(shù)。由于其復雜性,這些類型在生產級 Java 中很少使用。例如,調用 Method 需要對象引用(如果方法是非靜態(tài)的)或至少一個類標識符(如果方法是靜態(tài)的)。然后,被調用的 Method 的參數(shù)作為對象實例傳遞給它,如果沒有發(fā)生多態(tài)(那會出現(xiàn)另一種復雜性!),則可能需要顯式向下轉換。相比之下,lambda 和方法引用很容易作為參數(shù)傳遞給其它函數(shù)。

但是,新的函數(shù)式結構在流 API 之外具有其它用途。考慮一個 Java GUI 程序,該程序帶有一個供用戶按下的按鈕,例如,按下以獲取當前時間。按鈕按下的事件處理程序可能編寫如下:

  1. JButton updateCurrentTime = new JButton("Update current time");
  2. updateCurrentTime.addActionListener(new ActionListener() {
  3. @Override
  4. public void actionPerformed(ActionEvent e) {
  5. currentTime.setText(new Date().toString());
  6. }
  7. });

這個簡短的代碼段很難解釋。關注第二行,其中方法 addActionListener 的參數(shù)開始如下:

  1. new ActionListener() {

這似乎是錯誤的,因為 ActionListener 是一個抽象接口,而抽象類型不能通過調用 new 實例化。但是,事實證明,還有其它一些實例被實例化了:一個實現(xiàn)此接口的未命名內部類。如果上面的代碼封裝在名為 OldJava 的類中,則該未命名的內部類將被編譯為 OldJava$1.classactionPerformed 方法在這個未命名的內部類中被重寫。

現(xiàn)在考慮使用新的函數(shù)式結構進行這個令人耳目一新的更改:

  1. updateCurrentTime.addActionListener(e -> currentTime.setText(new Date().toString()));

lambda 表達式中的參數(shù) e 是一個 ActionEvent 實例,而 lambda 的主體是對按鈕上的 setText 的簡單調用。

函數(shù)式接口和函數(shù)組合

到目前為止,使用的 lambda 已經寫好了。但是,為了方便起見,我們可以像引用封裝方法一樣引用 lambda 表達式。以下一系列簡短示例說明了這一點。

考慮以下接口定義:

  1. @FunctionalInterface // 可選,通常省略
  2. interface BinaryIntOp {
  3. abstract int compute(int arg1, int arg2); // abstract 聲明可以被刪除
  4. }

注釋 @FunctionalInterface 適用于聲明唯一抽象方法的任何接口;在本例中,這個抽象接口是 compute。一些標準接口,(例如具有唯一聲明方法 runRunnable 接口)同樣符合這個要求。在此示例中,compute 是已聲明的方法。該接口可用作引用聲明中的目標類型:

  1. BinaryIntOp div = (arg1, arg2) -> arg1 / arg2;
  2. div.compute(12, 3); // 4

java.util.function 提供各種函數(shù)式接口。以下是一些示例。

下面的代碼段介紹了參數(shù)化的 Predicate 函數(shù)式接口。在此示例中,帶有參數(shù) StringPredicate<String> 類型可以引用具有 String 參數(shù)的 lambda 表達式或諸如 isEmpty 之類的 String 方法。通常情況下,Predicate 是一個返回布爾值的函數(shù)。

  1. Predicate<String> pred = String::isEmpty; // String 方法的 predicate 聲明
  2. String[] strings = {"one", "two", "", "three", "four"};
  3. Arrays.asList(strings)
  4. .stream()
  5. .filter(pred) // 過濾掉非空字符串
  6. .forEach(System.out::println); // 只打印空字符串

在字符串長度為零的情況下,isEmpty Predicate 判定結果為 true。 因此,只有空字符串才能進入管道的 forEach 階段。

下一段代碼將演示如何將簡單的 lambda 或方法引用組合成更豐富的 lambda 或方法引用。考慮這一系列對 IntUnaryOperator 類型的引用的賦值,它接受一個整型參數(shù)并返回一個整型值:

  1. IntUnaryOperator doubled = n -> n * 2;
  2. IntUnaryOperator tripled = n -> n * 3;
  3. IntUnaryOperator squared = n -> n * n;

IntUnaryOperator 是一個 FunctionalInterface,其唯一聲明的方法為 applyAsInt。現(xiàn)在可以單獨使用或以各種組合形式使用這三個引用 doubledtripledsquared

  1. int arg = 5;
  2. doubled.applyAsInt(arg); // 10
  3. tripled.applyAsInt(arg); // 15
  4. squared.applyAsInt(arg); // 25

以下是一些函數(shù)組合的樣例:

  1. int arg = 5;
  2. doubled.compose(squared).applyAsInt(arg); // 5 求 2 次方后乘 2:50
  3. tripled.compose(doubled).applyAsInt(arg); // 5 乘 2 后再乘 3:30
  4. doubled.andThen(squared).applyAsInt(arg); // 5 乘 2 后求 2 次方:100
  5. squared.andThen(tripled).applyAsInt(arg); // 5 求 2 次方后乘 3:75

函數(shù)組合可以直接使用 lambda 表達式實現(xiàn),但是引用使代碼更簡潔。

構造器引用

構造器引用是另一種函數(shù)式編程構造,而這些引用在比 lambda 和方法引用更微妙的上下文中非常有用。再一次重申,代碼示例似乎是最好的解釋方式。

考慮這個 POJO 類:

  1. public class BedRocker { // 基巖的居民
  2. private String name;
  3. public BedRocker(String name) { this.name = name; }
  4. public String getName() { return this.name; }
  5. public void dump() { System.out.println(getName()); }
  6. }

該類只有一個構造函數(shù),它需要一個 String 參數(shù)。給定一個名字數(shù)組,目標是生成一個 BedRocker 元素數(shù)組,每個名字代表一個元素。下面是使用了函數(shù)式結構的代碼段:

  1. String[] names = {"Fred", "Wilma", "Peebles", "Dino", "Baby Puss"};
  2.  
  3. Stream<BedRocker> bedrockers = Arrays.asList(names).stream().map(BedRocker::new);
  4. BedRocker[] arrayBR = bedrockers.toArray(BedRocker[]::new);
  5.  
  6. Arrays.asList(arrayBR).stream().forEach(BedRocker::dump);

在較高的層次上,這個代碼段將名字轉換為 BedRocker 數(shù)組元素。具體來說,代碼如下所示。Stream 接口(在包 java.util.stream 中)可以被參數(shù)化,而在本例中,生成了一個名為 bedrockersBedRocker 流。

Arrays.asList 實用程序再次用于流化一個數(shù)組 names,然后將流的每一項傳遞給 map 函數(shù),該函數(shù)的參數(shù)現(xiàn)在是構造器引用 BedRocker::new。這個構造器引用通過在每次調用時生成和初始化一個 BedRocker 實例來充當一個對象工廠。在第二行執(zhí)行之后,名為 bedrockers 的流由五項 BedRocker 組成。

這個例子可以通過關注高階 map 函數(shù)來進一步闡明。在通常情況下,一個映射將一個類型的值(例如,一個 int)轉換為另一個相同類型的值(例如,一個整數(shù)的后繼):

  1. map(n -> n + 1) // 將 n 映射到其后繼

然而,在 BedRocker 這個例子中,轉換更加戲劇化,因為一個類型的值(代表一個名字的 String)被映射到一個不同類型的值,在這個例子中,就是一個 BedRocker 實例,這個字符串就是它的名字。轉換是通過一個構造器調用來完成的,它是由構造器引用來實現(xiàn)的:

  1. map(BedRocker::new) // 將 String 映射到 BedRocker

傳遞給構造器的值是 names 數(shù)組中的其中一項。

此代碼示例的第二行還演示了一個你目前已經非常熟悉的轉換:先將數(shù)組先轉換成 List,然后再轉換成 Stream

  1. Stream<BedRocker> bedrockers = Arrays.asList(names).stream().map(BedRocker::new);

第三行則是另一種方式 —— 流 bedrockers 通過使用數(shù)組構造器引用 BedRocker[]::new 調用 toArray 方法:

  1. BedRocker[ ] arrayBR = bedrockers.toArray(BedRocker[]::new);

該構造器引用不會創(chuàng)建單個 BedRocker 實例,而是創(chuàng)建這些實例的整個數(shù)組:該構造器引用現(xiàn)在為 BedRocker[]:new,而不是 BedRocker::new。為了進行確認,將 arrayBR 轉換為 List,再次對其進行流式處理,以便可以使用 forEach 來打印 BedRocker 的名字。

  1. Fred
  2. Wilma
  3. Peebles
  4. Dino
  5. Baby Puss

該示例對數(shù)據(jù)結構的微妙轉換僅用幾行代碼即可完成,從而突出了可以將 lambda,方法引用或構造器引用作為參數(shù)的各種高階函數(shù)的功能。

柯里化Currying

柯里化函數(shù)是指減少函數(shù)執(zhí)行任何工作所需的顯式參數(shù)的數(shù)量(通常減少到一個)。(該術語是為了紀念邏輯學家 Haskell Curry。)一般來說,函數(shù)的參數(shù)越少,調用起來就越容易,也更健壯。(回想一下一些需要半打左右參數(shù)的噩夢般的函數(shù)!)因此,應將柯里化視為簡化函數(shù)調用的一種嘗試。java.util.function 包中的接口類型適合于柯里化,如以下示例所示。

引用的 IntBinaryOperator 接口類型是為函數(shù)接受兩個整型參數(shù),并返回一個整型值:

  1. IntBinaryOperator mult2 = (n1, n2) -> n1 * n2;
  2. mult2.applyAsInt(10, 20); // 200
  3. mult2.applyAsInt(10, 30); // 300

引用 mult2 強調了需要兩個顯式參數(shù),在本例中是 10 和 20。

前面介紹的 IntUnaryOperatorIntBinaryOperator 簡單,因為前者只需要一個參數(shù),而后者則需要兩個參數(shù)。兩者均返回整數(shù)值。因此,目標是將名為 mult2 的兩個參數(shù) IntBinraryOperator 柯里化成一個單一的 IntUnaryOperator 版本 curriedMult2

考慮 IntFunction<R> 類型。此類型的函數(shù)采用整型參數(shù),并返回類型為 R 的結果,該結果可以是另一個函數(shù) —— 更準確地說,是 IntBinaryOperator。讓一個 lambda 返回另一個 lambda 很簡單:

  1. arg1 -> (arg2 -> arg1 * arg2) // 括號可以省略

完整的 lambda 以 arg1 開頭,而該 lambda 的主體以及返回的值是另一個以 arg2 開頭的 lambda。返回的 lambda 僅接受一個參數(shù)(arg2),但返回了兩個數(shù)字的乘積(arg1arg2)。下面的概述,再加上代碼,應該可以更好地進行說明。

以下是如何柯里化 mult2 的概述:

  • 類型為 IntFunction<IntUnaryOperator> 的 lambda 被寫入并調用,其整型值為 10。返回的 IntUnaryOperator 緩存了值 10,因此變成了已柯里化版本的 mult2,在本例中為 curriedMult2
  • 然后使用單個顯式參數(shù)(例如,20)調用 curriedMult2 函數(shù),該參數(shù)與緩存的參數(shù)(在本例中為 10)相乘以生成返回的乘積。。

這是代碼的詳細信息:

  1. // 創(chuàng)建一個接受一個參數(shù) n1 并返回一個單參數(shù) n2 -> n1 * n2 的函數(shù),該函數(shù)返回一個(n1 * n2 乘積的)整型數(shù)。
  2. IntFunction<IntUnaryOperator> curriedMult2Maker = n1 -> (n2 -> n1 * n2);

調用 curriedMult2Maker 生成所需的 IntUnaryOperator 函數(shù):

  1. // 使用 curriedMult2Maker 獲取已柯里化版本的 mult2。
  2. // 參數(shù) 10 是上面的 lambda 的 n1。
  3. IntUnaryOperator curriedMult2 = curriedMult2Maker2.apply(10);

10 現(xiàn)在緩存在 curriedMult2 函數(shù)中,以便 curriedMult2 調用中的顯式整型參數(shù)乘以 10:

  1. curriedMult2.applyAsInt(20); // 200 = 10 * 20
  2. curriedMult2.applyAsInt(80); // 800 = 10 * 80

緩存的值可以隨意更改:

  1. curriedMult2 = curriedMult2Maker.apply(50); // 緩存 50
  2. curriedMult2.applyAsInt(101); // 5050 = 101 * 50

當然,可以通過這種方式創(chuàng)建多個已柯里化版本的 mult2,每個版本都有一個 IntUnaryOperator

柯里化充分利用了 lambda 的強大功能:可以很容易地編寫 lambda 表達式來返回需要的任何類型的值,包括另一個 lambda。

總結

Java 仍然是基于類的面向對象的編程語言。但是,借助流 API 及其支持的函數(shù)式構造,Java 向函數(shù)式語言(例如 Lisp)邁出了決定性的(同時也是受歡迎的)一步。結果是 Java 更適合處理現(xiàn)代編程中常見的海量數(shù)據(jù)流。在函數(shù)式方向上的這一步還使以在前面的代碼示例中突出顯示的管道的方式編寫清晰簡潔的 Java 代碼更加容易:

  1. dataStream
  2. .parallelStream() // 多線程以提高效率
  3. .filter(...) // 階段 1
  4. .map(...) // 階段 2
  5. .filter(...) // 階段 3
  6. ...
  7. .collect(...); // 或者,也可以進行歸約:階段 N

自動多線程,以 parallelparallelStream 調用為例,建立在 Java 的 fork/join 框架上,該框架支持 任務竊取task stealing 以提高效率。假設 parallelStream 調用后面的線程池由八個線程組成,并且 dataStream 被八種方式分區(qū)。某個線程(例如,T1)可能比另一個線程(例如,T7)工作更快,這意味著應該將 T7 的某些任務移到 T1 的工作隊列中。這會在運行時自動發(fā)生。

在這個簡單的多線程世界中,程序員的主要職責是編寫線程安全函數(shù),這些函數(shù)作為參數(shù)傳遞給在流 API 中占主導地位的高階函數(shù)。尤其是 lambda 鼓勵編寫純函數(shù)(因此是線程安全的)函數(shù)。 

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2023-08-31 16:47:05

反應式編程數(shù)據(jù)流

2011-12-14 15:57:13

javanio

2024-04-18 09:02:11

數(shù)據(jù)流Mixtral混合模型

2023-10-07 00:01:02

Java函數(shù)

2009-08-19 10:41:12

Java輸入數(shù)據(jù)流

2011-04-14 14:43:38

SSISTransformat

2014-02-11 08:51:15

亞馬遜PaaSAppStream

2009-07-15 09:06:11

Linux圖形系統(tǒng)X11的CS架構

2020-09-23 16:07:52

JavaScript函數(shù)柯里化

2011-04-19 09:18:02

SSIS數(shù)據(jù)轉換

2023-07-26 00:20:20

Java 8數(shù)組方式

2023-07-24 08:20:11

StreamJava方式

2010-06-22 13:32:26

函數(shù)式編程JavaScript

2021-06-29 19:24:42

數(shù)據(jù)流數(shù)據(jù)排序

2016-11-14 19:01:36

數(shù)據(jù)流聊天系統(tǒng)web

2024-12-05 10:37:36

Java純函數(shù)final

2010-08-03 08:54:07

JDK 7Lambda表達式函數(shù)式編程

2024-02-28 08:37:28

Lambda表達式Java函數(shù)式接口

2022-03-18 08:57:17

前端數(shù)據(jù)流選型

2021-10-27 10:43:36

數(shù)據(jù)流中位數(shù)偶數(shù)
點贊
收藏

51CTO技術棧公眾號

精品高清一区二区三区| 国产又黄又大久久| 中文字幕精品久久| 先锋资源在线视频| 欧美国产大片| 怡红院av一区二区三区| 欧美久久在线| 国产精品一区二区三区在线免费观看| 欧美成人中文| 日韩国产激情在线| 久久99爱视频| 羞羞的网站在线观看| 成人黄色777网| 国产91成人在在线播放| www久久久久久久| 亚洲1区在线| 精品国产乱码久久久久酒店| 视频三区二区一区| 丰满岳乱妇国产精品一区| 亚洲影院一区| 久久亚洲精品一区二区| 久久久精品高清| 福利小视频在线| 国产精品剧情在线亚洲| 国产精品久久久久av福利动漫| 黄色免费av网站| 日韩中文欧美| 欧美肥胖老妇做爰| 久久久久久久午夜| 精品视频在线一区二区| 91蝌蚪porny九色| 亚洲自拍偷拍色片视频| 黄色av一区二区| 激情久久久久久久| 深夜福利国产精品| 欧美高清性xxxx| 国产精品亚洲四区在线观看| 色婷婷久久久久swag精品| 精品少妇人妻av一区二区| 男女污污视频在线观看| 成人精品一区二区三区四区 | 色呦哟—国产精品| 亚洲精品美女久久| ass极品水嫩小美女ass| 日本美女久久| 一本久道中文字幕精品亚洲嫩| 91看片淫黄大片91| 欧美精品hd| 久久久99精品久久| 国内精品视频在线播放| 精品国产av一区二区三区| 日韩电影在线一区二区| 欧美在线性爱视频| 国产中文字幕免费| 欧美日韩视频一区二区三区| 日韩有码片在线观看| 一区二区三区少妇| 日韩成人精品| 日韩一级片网址| 久国产精品视频| 久久er热在这里只有精品66| 日本高清不卡在线观看| 欧美aⅴ在线观看| 国产不卡人人| 欧美日韩另类视频| 久艹视频在线免费观看| 国产经典三级在线| 一区二区三区鲁丝不卡| 91成人综合网| 国产一线二线在线观看 | 日韩女同互慰一区二区| 亚洲视频在线不卡| 欧美a视频在线| 欧美性感一类影片在线播放| 十八禁视频网站在线观看| 日本黄色免费在线| 色综合久久九月婷婷色综合| 国产裸体舞一区二区三区| 91久久国产综合久久91猫猫| 91久久精品国产91性色tv | 蜜桃麻豆影像在线观看| 欧美特级www| 熟女人妇 成熟妇女系列视频| 黄色成人免费网| 欧日韩精品视频| 亚洲成人天堂网| 亚洲欧美综合久久久久久v动漫| 欧美精品tushy高清| 男插女视频网站| 动漫视频在线一区| 亚洲欧美国产高清va在线播| 久久视频精品在线观看| 888久久久| 国内伊人久久久久久网站视频| 亚洲国产成人精品激情在线| 羞羞视频在线观看欧美| 国产精品福利在线观看网址| 91高潮大合集爽到抽搐| 国产福利一区二区| 精品欧美一区二区在线观看视频 | 日本中文一区二区三区| 国产有码在线一区二区视频| www国产一区| 99re这里都是精品| 亚洲欧洲久久| free性欧美| 91九色02白丝porn| 中文字幕乱码在线人视频| 尤物tv在线精品| 久久精品国产亚洲精品2020| 日本少妇xxxx动漫| 日本色综合中文字幕| 99久久自偷自偷国产精品不卡| 天天操天天干天天插| 国产精品五月天| 国产精品va在线观看无码| 亚洲深夜视频| 日韩限制级电影在线观看| 日本免费福利视频| 香蕉国产精品| 日本aⅴ大伊香蕉精品视频| 一级全黄少妇性色生活片| 本田岬高潮一区二区三区| 亚洲欧美日韩国产yyy| 美女高潮视频在线看| 337p亚洲精品色噜噜| 欧美日韩人妻精品一区在线| 日本电影一区二区| 1769国内精品视频在线播放| jizz中国女人| 国产人伦精品一区二区| 久艹视频在线免费观看| 免费成人毛片| 国产亚洲人成a一在线v站| 国产午夜精品无码一区二区| 久久99精品国产麻豆不卡| 欧美中文娱乐网| 免费av在线| 日本高清不卡视频| 性欧美丰满熟妇xxxx性久久久| 亚洲区综合中文字幕日日| 国产精品扒开腿做爽爽爽视频 | 国产乱码精品一区二区三区av| 欧美一级二级三级| 黄视频免费在线看| 欧美一区二区免费| 免费在线观看你懂的| 亚洲精一区二区三区| eeuss一区二区三区| www国产在线观看| 欧美精品久久久久久久久老牛影院| 37p粉嫩大胆色噜噜噜| 在线观看一区视频| 亚洲在线观看视频| 国产在线一区二区视频| 欧美日本免费一区二区三区| 亚洲精品91在线| 全国精品久久少妇| 视频一区国产精品| 777午夜精品电影免费看| 亚洲欧洲成视频免费观看| 国产成人免费观看视频| 成人av资源在线观看| 成年人视频网站免费| 亚洲国产视频二区| 色综合久久88色综合天天看泰| 国产精品久久久久久69| 亚洲女人的天堂| 天天干天天色天天干| 我不卡神马影院| 亚洲a级在线观看| 91最新在线视频| 日韩欧美一区二区免费| 九九热国产在线| 成人福利电影精品一区二区在线观看| 18黄暴禁片在线观看| 国语一区二区三区| 国产91成人video| 国产精品久久久久一区二区国产 | 国产日韩久久久| 欧美国产精品久久| 色呦色呦色精品| 欧美色图麻豆| 国新精品乱码一区二区三区18| 91白丝在线| 亚洲免费电影在线观看| 中文字幕乱码视频| 综合在线观看色| 少妇丰满尤物大尺度写真| 亚洲国产国产亚洲一二三| 久久精品欧美| av亚洲一区二区三区| www.欧美三级电影.com| 精品人妻久久久久一区二区三区| 亚洲大型综合色站| 国产熟女一区二区| 国产主播一区二区三区| 色欲色香天天天综合网www| 精品成人影院| 亚洲综合社区网| 三级在线看中文字幕完整版| 一区二区三区美女xx视频| 国产偷拍一区二区| 日韩欧美在线视频观看| 影音先锋男人资源在线观看| 成av人片一区二区| 亚洲天堂2018av| 亚洲精品三级| 日本特级黄色大片| 丝袜美腿综合| 成人网页在线免费观看| 中文字幕不卡三区视频| 久久视频这里只有精品| 天天插天天干天天操| 欧美精品在线一区二区三区| 亚洲精品视频在线观看免费视频| 欧美国产视频在线| 东京热av一区| 久久er99精品| 成人午夜激情av| 99精品久久久| 91网站在线观看免费| 欧美日韩国产免费观看视频| 风间由美一区二区三区| 欧美一区=区三区| 欧美性在线观看| 性直播体位视频在线观看| 尤物精品国产第一福利三区| 欧美自拍第一页| 69堂成人精品免费视频| 日韩av免费播放| 一区二区三区日韩欧美精品| 亚洲精品午夜视频| 成人一区二区三区视频| 最近中文字幕一区二区| 亚洲久色影视| 麻豆映画在线观看| 欧美丝袜激情| 日本视频一区二区不卡| 精品一区91| 国产精品亚洲精品| 唐人社导航福利精品| 97视频国产在线| 美女精品导航| 久久99久久99精品免观看粉嫩| 91视频在线观看| 在线观看成人黄色| 成人免费视频| 在线观看免费高清视频97| 欧洲视频在线免费观看| 亚洲第一页自拍| 亚洲国产精品久久久久爰性色| 91精品国产91综合久久蜜臀| 亚洲视频在线观看一区二区 | 色噜噜狠狠狠综合曰曰曰88av| 撸视在线观看免费视频| 亚洲免费视频一区二区| 午夜av免费观看| 日韩精品视频在线| 午夜激情在线视频| 精品一区二区电影| 亚洲人午夜射精精品日韩| 国产午夜精品理论片a级探花| 日韩一级片免费看| 亚洲精品一区二区三区婷婷月| 亚洲日本香蕉视频| 亚洲男人天堂古典| 国产综合视频一区二区三区免费| 亚洲天堂av在线播放| 国产精品视频一区二区久久| 中文在线不卡视频| 国产视频在线播放| 欧美韩日一区二区| 看黄在线观看| 国产精品99久久久久久久久| 国产成人福利夜色影视| 成人av番号网| 66精品视频在线观看| 国产福利不卡| 日日狠狠久久偷偷综合色| 精品蜜桃传媒| 日韩电影一区| 穿情趣内衣被c到高潮视频| 国产精品分类| 奇米影视亚洲色图| 蜜乳av另类精品一区二区| 天天操天天爽天天射| 国产一区二区美女| 中文字幕视频观看| 26uuu色噜噜精品一区| 精品人体无码一区二区三区| 亚洲免费观看高清完整版在线 | 99视频精品全部免费在线视频| 久久av秘一区二区三区| 黑人一区二区| 能在线观看的av网站| 国产中文一区二区三区| 国产真实乱人偷精品| 欧美高清在线一区二区| www青青草原| 欧美视频13p| 国产一区二区三区视频免费观看| 精品国产麻豆免费人成网站| 免费在线看v| 欧美寡妇偷汉性猛交| 波多野结衣久久精品| 91人人爽人人爽人人精88v| 羞羞色国产精品网站| 在线精品亚洲一区二区| 国产亚洲福利| 91在线第一页| 久久精品免视看| 少妇影院在线观看| 色94色欧美sute亚洲线路一久| 国产成人精品一区二三区四区五区| 亚洲精品色婷婷福利天堂| 自拍亚洲图区| 国产精品久久久久秋霞鲁丝 | 国产精品9999久久久久仙踪林| 欧美日本成人| 国产极品尤物在线| 狠狠色丁香婷婷综合久久片| 中文字幕一区三区久久女搜查官| 国产精品久久久久国产精品日日| 国产a∨精品一区二区三区仙踪林| 欧美日韩国产成人在线免费| 午夜影院免费视频| 久久伊人精品一区二区三区| 亚洲成人av观看| 精品91免费| 欧美日韩91| 久久成年人网站| 日本一区免费视频| 国产三级精品三级在线观看| 欧美成人高清电影在线| 免费网站免费进入在线| 国产成人一区二区三区小说| 国产成人澳门| 免费人成在线观看视频播放| 国产一区二区0| 疯狂试爱三2浴室激情视频| 欧美伊人久久久久久久久影院 | 欧日韩不卡视频| 一本色道久久加勒比精品| 日韩在线视频观看免费| 欧美成人亚洲成人| 疯狂欧洲av久久成人av电影| 图片区小说区区亚洲五月| 日韩国产欧美在线观看| 国产黄片一区二区三区| 午夜久久电影网| 亚洲卡一卡二卡三| 欧美极品少妇xxxxⅹ免费视频| 美女精品久久| 国产911在线观看| 国产精品白丝av| 深夜福利影院在线观看| 日韩一区二区在线播放| 成人看片免费| 国产精品美女免费视频| 中文字幕中文字幕精品| 亚洲中文字幕无码不卡电影| av电影在线观看一区| 91丝袜一区二区三区| 亚洲天堂av在线免费观看| 欧美与亚洲与日本直播| 亚洲国产一区二区精品视频| 蜜桃免费网站一区二区三区| 欧美波霸videosex极品| 欧美老女人第四色| 黄视频在线观看网站| 2022国产精品| 99成人免费视频| b站大片免费直播| 欧美性少妇18aaaa视频| 天堂中文在线8| 国产精品91久久| 欧美电影一二区| 丰满少妇一区二区三区专区| 亚洲成av人片在www色猫咪| 日韩av地址| 国产欧美日韩精品在线观看| 亚洲天天综合| 成人做爰69片免费| 精品久久久久久久久久久久| 韩国福利在线| 成人欧美一区二区三区在线| 国内精品美女在线观看| 熟女丰满老熟女熟妇| 欧美在线不卡一区| 色的视频在线免费看| 国产精品一区在线观看| 久久国产日本精品| 成人18视频免费69| 精品久久久久久久久久久久久久久| 美女av在线免费看| 在线成人性视频| 丁香婷婷综合激情五月色| 一级片在线观看免费| 欧美精品免费看|