国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java中怎么實(shí)現數據流和函數式編程

發(fā)布時(shí)間:2021-07-03 23:03 來(lái)源:億速云 閱讀:0 作者:Leah 欄目: 開(kāi)發(fā)技術(shù)

本篇文章為大家展示了Java中怎么實(shí)現數據流和函數式編程,內容簡(jiǎn)明扼要并且容易理解,絕對能使你眼前一亮,通過(guò)這篇文章的詳細介紹希望你能有所收獲。


基礎特性

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

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

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

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

流 API 提供了一種更簡(jiǎn)潔的方法來(lái)執行此操作:

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

這個(gè)例子有三個(gè)來(lái)自流 API 的函數:

  • stream 函數可以將集合轉換為流,而流是一個(gè)每次可訪(fǎng)問(wèn)一個(gè)值的傳送帶。流化是惰性的(因此也是高效的),因為值是根據需要產(chǎn)生的,而不是一次性產(chǎn)生的。

  • filter 函數確定哪些流的值(如果有的話(huà))通過(guò)了處理管道中的下一個(gè)階段,即 collect 階段。filter 函數是

    高階的higher-order

    ,因為它的參數是一個(gè)函數 &mdash;&mdash; 在這個(gè)例子中是一個(gè) lambda 表達式,它是一個(gè)未命名的函數,并且是 Java 新的函數式編程結構的核心。

lambda 語(yǔ)法與傳統的 Java 完全不同:

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

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

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

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

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

在生產(chǎn)環(huán)境中,數據流的源可能是文件或網(wǎng)絡(luò )連接。為了學(xué)習流 API, Java 提供了諸如 IntStream 這樣的類(lèi)型,它可以用各種類(lèi)型的元素生成流。這里有一個(gè) IntStream 的例子:

IntStream                          // 整型流    .range(1, 2048)                // 生成此范圍內的整型流    .parallel()                    // 為多個(gè)線(xiàn)程分區數據    .filter(i -> ((i & 0x1) > 0))  // 奇偶校驗 - 只允許奇數通過(guò)    .forEach(System.out::println); // 打印每個(gè)值

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

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

map/reduce 模式

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

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

public class Number {    enum Parity { EVEN, ODD }    private int value;    public Number(int n) { setValue(n); }    public void setValue(int value) { this.value = value; }    public int getValue() { return this.value; }    public Parity getParity() {        return ((value & 0x1) == 0) ? Parity.EVEN : Parity.ODD;    }    public void dump() {        System.out.format("Value: %2d (parity: %s)\n", getValue(),                          (getParity() == Parity.ODD ? "odd" : "even"));    }}

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

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

final int howMany = 200;Random r = new Random();Number[] nums = new Number[howMany];for (int i = 0; i < howMany; i++) nums[i] = new Number(r.nextInt(100));List<Number> listOfNums = Arrays.asList(nums);  // 將數組轉化為 list Integer sum4All = listOfNums    .parallelStream()           // 自動(dòng)執行多線(xiàn)程    .mapToInt(Number::getValue) // 使用方法引用,而不是 lambda    .sum();                     // 將流值計算出和值System.out.println("The sum of the randomly generated values is: " + sum4All);

高階的 mapToInt 函數可以接受一個(gè) lambda 作為參數,但在本例中,它接受一個(gè)方法引用,即 Number::getValue。getValue 方法不需要參數,它返回給定的 Number 實(shí)例的 int 值。語(yǔ)法并不復雜:類(lèi)名 Number 后跟一個(gè)雙冒號和方法名?;叵胍幌孪惹暗睦?System.out::println,它在 System 類(lèi)中的 static 屬性 out 后面有一個(gè)雙冒號。

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

mapToInt(n -> n.getValue())

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

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

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

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

Integer sum4AllHarder = listOfNums    .parallelStream()                           // 多線(xiàn)程    .map(Number::getValue)                      // 每個(gè) Number 的值    .reduce(0, (sofar, next) -> sofar + next);  // 求和

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

  • 第一個(gè)參數(在這種情況下為零)是特征值,該值用作求和操作的初始值,并且在求和過(guò)程中流結束時(shí)用作默認值。

  • 第二個(gè)參數是累加器,在本例中,這個(gè) lambda 表達式有兩個(gè)參數:第一個(gè)參數(sofar)是正在運行的和,第二個(gè)參數(next)是來(lái)自流的下一個(gè)值。運行的和以及下一個(gè)值相加,然后更新累加器。請記住,由于開(kāi)始時(shí)調用了 parallelStream,因此 mapreduce 函數現在都在多線(xiàn)程上下文中執行。

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

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

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

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

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

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

The sum of the randomly generated values is: 3322The sum again, using a different method:     3322 Evens: Value: 72 (parity: even)Value: 54 (parity: even)...Value: 92 (parity: even) Odds: Value: 35 (parity: odd)Value: 37 (parity: odd)...Value: 41 (parity: odd)

用于代碼簡(jiǎn)化的函數式結構

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

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

JButton updateCurrentTime = new JButton("Update current time");updateCurrentTime.addActionListener(new ActionListener() {    @Override    public void actionPerformed(ActionEvent e) {        currentTime.setText(new Date().toString());    }});

這個(gè)簡(jiǎn)短的代碼段很難解釋。關(guān)注第二行,其中方法 addActionListener 的參數開(kāi)始如下:

new ActionListener() {

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

現在考慮使用新的函數式結構進(jìn)行這個(gè)令人耳目一新的更改:

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

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

函數式接口和函數組合

到目前為止,使用的 lambda 已經(jīng)寫(xiě)好了。但是,為了方便起見(jiàn),我們可以像引用封裝方法一樣引用 lambda 表達式。以下一系列簡(jiǎn)短示例說(shuō)明了這一點(diǎn)。

考慮以下接口定義:

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

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

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

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

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

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

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

下一段代碼將演示如何將簡(jiǎn)單的 lambda 或方法引用組合成更豐富的 lambda 或方法引用??紤]這一系列對 IntUnaryOperator 類(lèi)型的引用的賦值,它接受一個(gè)整型參數并返回一個(gè)整型值:

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

IntUnaryOperator 是一個(gè) FunctionalInterface,其唯一聲明的方法為 applyAsInt?,F在可以單獨使用或以各種組合形式使用這三個(gè)引用 doubled、tripledsquared

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

以下是一些函數組合的樣例:

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

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

構造器引用

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

考慮這個(gè) POJO 類(lèi):

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

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

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

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

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

這個(gè)例子可以通過(guò)關(guān)注高階 map 函數來(lái)進(jìn)一步闡明。在通常情況下,一個(gè)映射將一個(gè)類(lèi)型的值(例如,一個(gè) int)轉換為另一個(gè)相同類(lèi)型的值(例如,一個(gè)整數的后繼):

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

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

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

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

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

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

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

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

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

FredWilmaPeeblesDinoBaby Puss

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

柯里化Currying

柯里化函數是指減少函數執行任何工作所需的顯式參數的數量(通常減少到一個(gè))。(該術(shù)語(yǔ)是為了紀念邏輯學(xué)家 Haskell  Curry。)一般來(lái)說(shuō),函數的參數越少,調用起來(lái)就越容易,也更健壯。(回想一下一些需要半打左右參數的噩夢(mèng)般的函數?。┮虼?,應將柯里化視為簡(jiǎn)化函數調用的一種嘗試。java.util.function 包中的接口類(lèi)型適合于柯里化,如以下示例所示。

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

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

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

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

考慮 IntFunction<R> 類(lèi)型。此類(lèi)型的函數采用整型參數,并返回類(lèi)型為 R 的結果,該結果可以是另一個(gè)函數 &mdash;&mdash; 更準確地說(shuō),是 IntBinaryOperator。讓一個(gè) lambda 返回另一個(gè) lambda 很簡(jiǎn)單:

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

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

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

  • 類(lèi)型為 IntFunction<IntUnaryOperator> 的 lambda 被寫(xiě)入并調用,其整型值為 10。返回的 IntUnaryOperator 緩存了值 10,因此變成了已柯里化版本的 mult2,在本例中為 curriedMult2。

  • 然后使用單個(gè)顯式參數(例如,20)調用 curriedMult2 函數,該參數與緩存的參數(在本例中為 10)相乘以生成返回的乘積。。

這是代碼的詳細信息:

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

調用 curriedMult2Maker 生成所需的 IntUnaryOperator 函數:

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

10 現在緩存在 curriedMult2 函數中,以便 curriedMult2 調用中的顯式整型參數乘以 10:

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

緩存的值可以隨意更改:

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

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

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

總結

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

dataStream   .parallelStream() // 多線(xiàn)程以提高效率   .filter(...)      // 階段 1   .map(...)         // 階段 2   .filter(...)      // 階段 3   ...   .collect(...);    // 或者,也可以進(jìn)行歸約:階段 N

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

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

久久久久人妻一区精品性色AV| 国产sm重味一区二区三区| 亚洲AV超清无码不卡在线网络| 性高湖久久久久久久久AAAAA| 精品久久久久中文字幕一区| 国产末成年女av片|