日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java 8 Lambda 表达式(副作用)

發布時間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 8 Lambda 表达式(副作用) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【1】轉自: https://www.cnblogs.com/linlinismine/p/9283532.html

?早在2014年oracle發布了jdk 8,在里面增加了lambda模塊。于是java程序員們又多了一種新的編程方式:函數式編程,也就是lambda表達式。我自己用lambda表達式也差不多快4年了,但在工作中卻鮮有看到同事使用這種編程方式,即使有些使用了,但感覺好像對其特性也不是很了解。我看了一上網上的資料也不少,自己整理了一下順便寫下一些自己的看法,希望我的分享能帶給別人一些幫助。

? ??函數式編程基本概念入門

  • ? ?什么是函數式編程?

? ? ????函數式編程(英語:functional programming)或稱函數程序設計,又稱泛函編程,是一種編程典范,它將電腦運算視為數學上的函數計算,并且避免使用程序狀態以及易變對象。函數編程語言最重要的基礎是λ演算(lambda calculus)。而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值)。比起指令式編程,函數式編程更加強調程序執行的結果而非執行的過程,倡導利用若干簡單的執行單元讓計算結果不斷漸進,逐層推導復雜的運算,而不是設計一個復雜的執行過程。這是維基百科給出的定義。從這個我們知道函數式編程是相對于指令式編程的一種編程典范,并且對而言具有一些優點。

  • ??函數式編程的特性與優缺點

? ? ??特性

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

? ? ? ? ?什么是"第一等公民"?所謂"第一等公民"(first class),指的是函數與其他數據類型一樣,處于平等地位,它不僅擁有一切傳統函數的使用方式(聲明和調用),可以賦值給其他變量(賦值),也可以作為參數,傳入另一個函數(傳參),或者作為別的函數的返回值(返回)。函數可以作為參數進行傳遞,意味我們可以把行為"參數化",處理邏輯可以從外部傳入,這樣程序就可以設計得更靈活。

? ? ?2、沒有"副作用"

? ? ?所謂"副作用"(side effect),指的是函數內部與外部互動(最典型的情況,就是修改全局變量的值),產生運算以外的其他結果。函數式編程強調沒有"副作用",意味著函數要保持獨立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。

? ? 3、引用透明

? ? 引用透明(Referential transparency),指的是函數的運行不依賴于外部變量或"狀態",只依賴于輸入的參數,任何時候只要參數相同,引用函數所得到的返回值總是相同的。這里強調了一點"輸入"不變則"輸出"也不變,就像數學函數里面的f(x),只要輸入的x一樣那得到的結果也肯定定是一樣的。

? ? ? 優點:

? ? 1、代碼簡潔,開發快速。

? ? ?函數式編程大量使用函數,減少了代碼的重復,因此程序比較短,開發速度較快。Paul Graham在《黑客與畫家》一書中寫道:同樣功能的程序,極端情況下,Lisp代碼的長度可能是C代碼的二十分之一。如果程序員每天所寫的代碼行數基本相同,這就意味著,"C語言需要一年時間完成開發某個功能,Lisp語言只需要不到三星期。反過來說,如果某個新功能,Lisp語言完成開發需要三個月,C語言需要寫五年。"當然,這樣的對比故意夸大了差異,但是"在一個高度競爭的市場中,即使開發速度只相差兩三倍,也足以使得你永遠處在落后的位置。"?

? ? 2. 接近自然語言,易于理解

?? ? ? 函數式編程的自由度很高,可以寫出很接近自然語言的代碼。以java為例把學生以性別分組:

?? ? ? 沒用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);

????????}

  用了lambda表達式:

1

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

?? ? ? 這基本就是自然語言的表達了,大家應該一眼就能明白它的意思吧。?

? ? ? 3. 更方便的代碼管理

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

? ? ? 4. 易于"并發編程"

? ? ? 函數式編程不需要考慮"死鎖"(deadlock),因為它不修改變量,所以根本不存在"鎖"線程的問題。不必擔心一個線程的數據,被另一個線程修改,所以可以很放心地把工作分攤到多個線程,部署"并發編程"(concurrency)。

? ? ?請看下面的代碼:

? ? ?var s1 = Op1();

? ? ?var s2 = Op2();

? ? ?var s3 = concat(s1, s2);

? ? ?由于s1和s2互不干擾,不會修改變量,誰先執行是無所謂的,所以可以放心地增加線程,把它們分配在兩個線程上完成。其他類型的語言就做不到這一點,因為s1可能會修改系統狀態,而s2可能會用到這些狀態,所以必須保證s2在s1之后運行,自然也就不能部署到其他線程上了。多核CPU是將來的潮流,所以函數式編程的這個特性非常重要。

? ? ? 5. 代碼的熱升級

?? ???函數式編程沒有副作用,只要保證接口不變,內部實現是外部無關的。所以,可以在運行狀態下直接升級代碼,不需要重啟,也不需要停機。Erlang語言早就證明了這一點,它是瑞典愛立信公司為了管理電話系統而開發的,電話系統的升級當然是不能停機的。

? ? ?缺點:

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

  • 早期的函數式編程語言實現時并無考慮過效率問題。

  • 有些非函數式編程語言為求提升速度,不提供自動邊界檢查或自動垃圾回收等功能。

? ? 惰性求值亦為語言如Haskell增加了額外的管理工作。

?? ?

? ? 2、語言學習曲線陡峭,難度高

? ? 函數式語言對開發者的要求比較高,學習曲線比較陡,而且很容易因為其靈活的語法控制不好程序的結構。

? ? ?介紹完函數式編程的概念和優缺點之后,下面讓我們來進入java8 lambda的編程世界~

?

? ? ? Lambda表達式的組成

? ? ???java 8 中Lambda 表達式由三個部分組成:第一部分為一個括號內用逗號分隔的形式參數,參數是函數式接口里面方法的參數;第二部分為一個箭頭符號:->;第三部分為方法體,可以是表達式和代碼塊。語法如下

? ? ? ?1、方法體為表達式,該表達式的值作為返回值返回。

1

2

(parameters) -> expression

(int?a,int?b) ->?return?a + b;?//求和

? ? ? ?2、方法體為代碼塊,必須用 {} 來包裹起來,且需要一個 return 返回值,但若函數式接口里面方法返回值是 void,則無需返回值。

1

2

3

(parameters) -> { statements; }

(int?a) -> {System.out.println("a = "?+ a);}?//打印,無返回值

(int?a) -> {return?a * a;}?//求平方

? ? ?

? ? ? Lambda表達式的底層實現

??????java 8 內部Lambda 表達式的實現方式在本質是以匿名內部類的形式的實現的,看下面代碼。代碼中我們定義了一個叫binaryOperator的Lambda表達式,看返回值它是一個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

 ? ?我們再看一下IntBinaryOperator的定義?

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);

}

? ? ???

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

??? ?Lambda表達式的函數式接口?

?????上面提到了函數式接口,那這是一個什么樣的概念呢?

? ? ? 函數式接口(Functional Interface)是Java 8對一類特殊類型的接口的稱呼。這類接口只定義了唯一的抽象方法的接口(除了隱含的Object對象的公共方法,因此最開始也就做SAM類型的接口(Single Abstract Method)。定義函數式接口的原因是在Java Lambda的實現中,開發組不想再為Lambda表達式單獨定義一種特殊的Structural函數類型,稱之為箭頭類型(arrow type,依然想采用Java既有的類型(class, interface, method等).原因是增加一個結構化的函數類型會增加函數類型的復雜性,破壞既有的Java類型,并對成千上萬的Java類庫造成嚴重的影響。權衡利弊,因此最終還是利用SAM 接口作為 Lambda表達式的目標類型.另外對于函數式接口來說@FunctionalInterface并不是必須的,只要接口中只定義了唯一的抽象方法的接口那它就是一個實質上的函數式接口,就可以用來實現Lambda表達式。

? ? ? ?在java 8中已經為我們定義了很多常用的函數式接口它們都放在java.util.function包下面,一般有以下常用的四大核心接口:? ? ? ?

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

? ??Lambda表達式的應用場景

? ? ?1、使用() -> {} 替代匿名類

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"));

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

? ? ?2、以流水線的方式處理數據

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個元素 [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();//求和

Set<Integer> integersSet = integers.stream().collect(Collectors.toSet());//轉成其它數據結構比如set

Map<Boolean, List<Integer>> listMap = integers.stream().collect(Collectors.groupingBy(i -> i %?2?==?0));?//根據奇偶性分組

List<Integer> list = integers.stream().filter(i -> i %?2?==?0).map(i -> i * i).distinct().collect(Collectors.toList());//復合操作

  借助stream api和Lambda表達式,以住需要定義多個變量,編寫數十行甚至數百行的代碼的集合操作,現在都基本簡化成了可以在一行之內完成~

? ? ? 3、更簡單的數據并行處理

1

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

 ? ?數據并行處理,只需要在原來的基礎上加一個parallel()就可以開啟~。順便提一下這里parallel()開啟的底層并行框架是fork/join,默認的并行數是Ncpu個。

? ? ? 4、用內部迭代取代外部迭代

? ? ? ?外部迭代:描述怎么干,代碼里嵌套2個以上的for循環的都比較難讀懂;只能順序處理List中的元素;

? ? ? ?內部迭代:描述要干什么,而不是怎么干;不一定需要順序處理List中的元素

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、重構現有臃腫代碼,更高的開發效率

? ? ??在Lambda表達式出現之前,我們的處理邏輯只能是以命令式編程的方式來實現,需要大量的代碼去編寫程序的每一步操作,定義非常多的變量,代碼量和工作量都相對的巨大。如果用Lambda表達式我們看到以往數十行甚至上百行的代碼都可以濃縮成幾行甚至一行代碼。這樣處理邏輯就會相對簡單,開發效率可以得到明顯提高,維護工作也相對容易。

? ? ? ?Lambda表達式中的Stream

? ? ? ? 在java 8 中 Stream 不是集合元素,它不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素并對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什么操作,比如 “過濾掉長度大于 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

? ? ? ? Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完后再讀下一個 item。而使用并行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。Stream 的并行操作依賴于 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。

? ? ? ? Stream可以有限的也可以是無限的,流的構造方式有很多可以從常用的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())

? ? ? ?上面的這些Stream都是有限的,我們可以用函數來創建一個無限Stream

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

? ? ? ? Stream也很懶惰,它只會在你真正需要數據的時候才會把數據給傳給你,在你不需要時它一個數據都不會產生。

? ? ? ?Lambda表達式的Best Practice

? ? ? ? 1、保持Lambda表達式簡短和一目了然

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());??//代碼復雜難懂 

1

2

3

values.stream()

??.mapToInt(e -> sumOfFactors(e))

??.sum()?//代碼簡潔一目了然

  長長的Lambda表達式通常是危險的,因為代碼越長越難以讀懂,意圖看起來也不明,并且代碼也難以復用,測試難度也大。

? ? ? 2、使用@FunctionalInterface?注解

? ? ? ? ?如果你確定了某個interface是用于Lambda表達式,請一定要加上@FunctionalInterface,表明你的意圖。不然將來說不定某個不知情的家伙比如你旁邊的好基友,在這個interface上面加了另外一個抽像方法時,你的代碼就悲劇了。

? ??? 3、優先使用java.util.function包下面的函數式接口

? ? ??? ?java.util.function?這個包下面提供了大量的功能性接口,可以滿足大多數開發人員為lambda表達式和方法引用提供目標類型的需求。每個接口都是通用的和抽象的,使它們易于適應幾乎任何lambda表達式。開發人員應該在創建新的功能接口之前研究這個包,避免重復定義接口。另外一點就是,里面的接口不會被別人修改~。

? ? ??4、不要在Lambda表達中執行有"副作用"的操作

? ? ? ? "副作用"是嚴重違背函數式編程的設計原則,在工作中我經常看到有人在forEach操作里面操作外面的某個List或者設置某個Map這其實是不對的。

? ??? 5、不要把Lambda表達式和匿名內部類同等對待

? ? ? ? ?雖然我們可以用匿名內部類來實現Lambda表達式,也可以用Lambda表達式來替換內部類,但并不代表這兩者是等價的。這兩者在某一個重要概念是不同的:this指代的上下文是不一樣的。當您使用內部類時,它將創建一個新的范圍。通過實例化具有相同名稱的新局部變量,可以從封閉范圍覆蓋局部變量。您還可以在內部類中使用這個關鍵字作為它實例的引用。但是,lambda表達式可以使用封閉范圍。您不能在lambda的主體內覆蓋范圍內的變量

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;

}

   運行上面這段代碼我們將到?resultIC = "Inner class value",resultLambda =?"Enclosing scope value"。也就是說在匿名內部類中this指的是自身的引用,在Lambda表達式中this指的是外部。

? ? ? ?6、多使用方法引用

? ? ? ?在Lambda表達式中 a -> a.toLowerCase()和String::toLowerCase都能起到相同的作用,但兩者相比,后者通常可讀性更高并且代碼會簡短。

? ? ? ?7、盡量避免在Lambda的方法體中使用{}代碼塊

? ? ? ?優先使用

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、不要盲目的開啟并行流

? ? ? ?Lambda的并行流雖好,但也要注意使用場景。如果平常的業務處理比如過濾,提取數據,沒有涉及特別大的數據和耗時操作,則真的不需要開啟并行流。我在工作中看到有些人一個只有幾十個元素的列表的過濾操作也開啟了并行流,其實這樣做會更慢。因為多行線程的開啟和同步這些花費的時間往往比你真實的處理時間要多很多。但一些耗時的操作比如I/O訪問,DB查詢,遠程調用,這些如果可以并行的話,則開啟并行流是可提升很大性能的。因為并行流的底層原理是fork/join,如果你的數據分塊不是很好切分,也不建議開啟并行流。舉個例子ArrayList的Stream可以開啟并行流,而LinkedList則不建議,因為LinkedList每次做數據切分要遍歷整個鏈表,這本身就已經很浪費性能,而ArrayList則不會。

總結

以上是生活随笔為你收集整理的java 8 Lambda 表达式(副作用)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲精品成人无码熟妇在线 | 蜜桃色999 | 欧美视频一区二区在线观看 | 日本打屁股网站 | 原神女裸体看个够无遮挡 | 国产69精品久久久久久久久久 | 女生张开腿给男生桶 | 日韩欧美高清片 | 日韩精品一区不卡 | 无人码人妻一区二区三区免费 | 亚洲午夜av | 亚洲国产日韩一区无码精品久久久 | 免费av中文字幕 | 国产三级视频在线播放 | 日本高清在线一区 | 黄色大片免费的 | 欧美亚洲少妇 | 久久在线免费 | 久久久久人妻精品色欧美 | 久久丝袜视频 | 第一次破处视频 | 日韩美女爱爱 | 日日插插 | 国产中文字幕精品 | 色牛av| 国产一区二区三区在线免费观看 | 国产宾馆实践打屁股91 | 精彩视频一区二区三区 | 色天堂影院| aaa成人| 性色av蜜臀av浪潮av老女人 | 精品久久久一区二区 | 欧美男同又粗又长又大 | 亚洲国产一二三区 | 色哟哟在线视频 | 精品国模一区二区三区 | 久久久久免费观看 | 在办公室被c到呻吟的动态图 | 96在线观看| 操操操免费视频 | 不卡日本 | 国产日韩欧美电影 | 爱爱视频欧美 | 抖音视频在线观看 | 欧美色频 | 丁香久久综合 | 欧美在线免费播放 | 制服丝袜av电影 | 韩日激情视频 | 国产一级精品视频 | 福利电影一区 | 一道本视频在线 | 污视频在线观看免费 | 亚洲一区二区人妻 | av不卡在线观看 | 国产免费黄色片 | 亚洲 精品 综合 精品 自拍 | 天堂在线观看免费视频 | av免费大全 | 欧美一区二不卡视频 | 天天操,夜夜操 | 亚洲av无码乱码国产精品fc2 | 久久成人精品 | 午夜伦理一区二区 | 最新av免费观看 | 午夜激情av| 亚洲一区二区中文 | 国产久在线| 亚洲AV无码精品一区二区三区 | 呦呦视频在线观看 | 99精品国产成人一区二区 | 日本在线免费 | 久久久久香蕉视频 | 亚洲男人的天堂在线观看 | 日本免费观看视频 | 日韩短视频 | 少妇高潮一区二区三区99小说 | 91高清视频免费观看 | 日韩免费专区 | 久久.com | 青草热视频 | 天天拍天天色 | 97久久久久久久久久 | av免费播放网站 | 中文字幕2区 | 亚洲日本精品一区 | 另类视频在线观看 | 国产精品高潮AV无码 | 色悠久久综合 | 丰满少妇乱子伦精品看片 | 国产艳妇疯狂做爰视频 | 黄色高清无遮挡 | 老司机午夜精品视频 | 大陆熟妇丰满多毛xxxⅹ | 日本九九热 | av国产网站 | 国产破处av | wwwwxxxxx日本 | 精品久久久一区 |