函数式编程[Lambda 表达式,Optional,Stream流]从入门到精通(一)
生活随笔
收集整理的這篇文章主要介紹了
函数式编程[Lambda 表达式,Optional,Stream流]从入门到精通(一)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 函數式編程、stream流
- 1.概述
- 1.1 學習目的
- 1.2 函數式編程思想
- 2.lambda表達式
- 2.1 概述
- 2.2 省略規則
- 3. stream流
- 3.1 概述
- 3.2 功能
- 3.3 常用方法說明 com/jonny/stream/StreamDemo.java
- 3.4 常用操作
- 3.4.1 創建流
- 3.4.2 中間操作
- 3.4.3 終結操作
- 3.5 注意事項
函數式編程、stream流
1.概述
1.1 學習目的
- 代碼可讀性
- 避免過分嵌套
- 看懂別人寫的代碼
- 大數據量下集合處理效率
- 底層使用多線程處理并線程安全可以保障?
1.2 函數式編程思想
- 面向對象思想主要是關注對象能完成什么事情,函數式編程思想就像函數式,主要是針對數據操作;
- 代碼簡潔容易理解,方便于并發編程,不需要過分關注線程安全問題
2.lambda表達式
2.1 概述
- lambda是JDK8中的一個語法糖,可以對某些匿名內部類的寫法進行優化,讓函數式編程只關注數據而不是對象。
- 基本格式:(參數列表)->{方法體}
2.2 省略規則
- 參數類型可以省略
- 方法體只有一句代碼時,大括號和 return 以及 唯一代碼的分號可以省略
- 方法只有一個參數時[即,參數列表只有一個參數時] 小括號可以省略
- 如果上面這些記不住,可以不省略也行
3. stream流
3.1 概述
- stream使用的是函數式編程模式,可以被用來對集合或數組進行鏈狀流式的操作
- 有別于其他輸入輸出流,這里是針對集合操作數據的流哦
- 創建流實戰:com.jonny.stream.StreamDemo
- 練習一:
- 打印年齡小于18歲的作者名字,并去重
- /** 練習一: 打印年齡小于18的作者姓名,并且去重*/getAuthors().stream().distinct().filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge() < 18;}}).forEach(new Consumer<Author>() {@Overridepublic void accept(Author author) {System.out.println(author.getName());}});
- 練習一:
3.2 功能
- 流不存儲元素。它只是通過計算操作的流水線從數據結構, 數組或I/O通道等源中傳遞元素。
- 流本質上是功能性的。對流執行的操作不會修改其源。例如, 對從集合中獲取的流進行過濾會產生一個新的不帶過濾元素的流, 而不是從源集合中刪除元素。
- Stream是惰性的, 僅在需要時才評估代碼。
- 在流的生存期內, 流的元素只能訪問一次。像Iterator一樣, 必須生成新的流以重新訪問源中的相同元素。
3.3 常用方法說明 com/jonny/stream/StreamDemo.java
抽取方法快捷鍵: Mac:[? + ? + M] Win: ctrl + alt + M
顯示方法參數提示快捷鍵 : ? + P
- map:相當于對數據進行一個操作,可以自定義返回值等
- distinct:可以去除流中的相同元素,注意(該方法依賴的Object的equals方法來判斷是否是相同對象,所以要重寫equals方法,否則只有對象地址一樣時才會被認為是重復)
- sorted:可以對流中的元素進行排序,傳入空參時使用的是實體類的比較方法
- limit:設置流的最大長度,超出部分將被拋棄
- skip:跳過流中的前n個元素,返回剩下的元素
- flatMap:map能把一個對象轉換成另外一個對象來作為流中的元素,而flatMap可以把一個對象轉換成多個對象作為流中的元素
- 中間操作(filter,map,distinct,sorted,limit,skip,flatMap)
- 終結操作(forEach,collect,count,max,min,reduce歸并,查找與匹配)
- forEach:遍歷所有元素
- count:計算元素數量
- min&max:返回的是option對象,這里和sorted一樣,得指定比較規則
- collect:把當前流轉換成一個集合(list, set, map)
- Collectors.toList()
- Collectors.toSet()
- Collectors.toMap(key, value)
- anyMatch:可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型
- allMatch:可以用來判斷是否都匹配條件,結果也是boolean類型,都符合則為true
- noneMatch:是否都不符合,都不符合則為true
- findAny:獲取流中的任意一個元素,該方法無法保證獲取的是流中的第一個元素,只是匹配到
- findFirst:獲取流中的第一個元素
- reduce:對流中的數據按照你制定的計算方式計算出一個結果,并返回一個Optional描述歸約值(如果有)T result = identity; for(T element : this stream) {result = accumulator.apply(result, element); // 執行具體數據操作 } return result; // 還有一種三個方法的重載方法,后面還需要補充
3.4 常用操作
3.4.1 創建流
- 單列集合:集合對象.stream()
- List<Author> authors = getAuthors();Stream<Author> stream = authors.stream();
- 數組: Arrays.stream(數組)或者Stream.of(數組)
- Integer[] arr = {1,2,3,4,5};Stream<Integer> stream = Arrays.stream(arr);Stream<Integer> stream2 = Stream.of(arr);
- 雙列集合:轉換成單列集合再創建
- Map<String, Integer> map = new HashMap<>();map.put("蠟筆小新",19);map.put("黑子",17);map.put("日向翔陽",16);Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
3.4.2 中間操作
-
filter
- 對流中元素進行過濾,符合條件的元素才可以留在流中
- // 例如打印作家姓名長度小于 3 的作家名 authors.stream().filter(author -> author.getName().length() < 3).forEach(author -> System.out.println("作家名: " + author.getName()));
-
map
- 可以對流中的元素進行計算或者轉換
- // 例如:打印所有作家的姓名List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).forEach(name -> System.out.println(name));// 將所有的作家年齡?10并輸出authors.stream().map(author -> author.getAge()).map(age -> age+10 ).forEach(age -> System.out.println(age));
-
distinct
- 可以去除流中的重復元素
- 注意:
- distinct方法是依賴于Object 方法的equals 方法來判斷是否相同對象的
- 而 Object 的equals 方法是 == 判斷的是兩個對象的地址是否相同
- 所以要看傳入類型是否有重寫 equals 方法,
- 沒有重寫則是繼承 Object 的 equals 方法,需要手動重寫
- 注意:
- // 打印所有作家的姓名,并且不能有重復元素List<Author> authors = getAuthors();authors.stream().distinct().forEach(author -> System.out.println(author.getName()));
- 可以去除流中的重復元素
-
sorted
- 可以對流中的元素進行排序
- // 對流中的元素按年齡進行排序,并且去除重復項 List<Author> authors = getAuthors();authors.stream().distinct().sorted((o1, o2) -> {// return o1.getAge() - o2.getAge(); // 升序return o2.getAge() - o1.getAge(); // 降序}).forEach(author -> System.out.println(author.getAge()));
- 注意
- 如果調用空參的 sorted 的方法,需要流中的元素實現 Comparable接口
-
limit
- 可以設置流的最大長度,超出部分將被拋棄
- 例如:
對流中的元素按照年齡進行降序排序,并且要求不能有重復的元素,然后打印其中年齡最大的兩個作家的姓名。- List<Author> authors = getAuthors();authors.stream().distinct().sorted((o1, o2) -> o2.getAge() - o1.getAge()).limit(2).forEach(author -> System.out.println(author.getName()));}
-
skip
- 跳過流中的前n個元素,返回剩下的元素
- 例如:
打印除了年齡最大的作家外的其他作家,要求不能有重復元素,并且按照年齡降序排序。- List<Author> authors = getAuthors();authors.stream().distinct().sorted((o1, o2) -> o2.getAge() - o1.getAge()).skip(1).forEach(author -> System.out.println(author.getName()));
-
flatMap
- map只能把一個對象轉換成另一個對象來作為流中的元素。
- 而flatMap可以把一個對象轉換成多個對象作為流中的元素。
- 例一:
打印所有書籍的名字。要求對重復的元素進行去重。- List<Author> authors = getAuthors();authors.stream().flatMap(author -> author.getBookList().stream()) // 將Author流轉換成Book流.distinct() // 去重.forEach(book -> System.out.println(book.getName()));
- 例二:
打印現有數據的所有分類。要求對分類進行去重。不能出現這種格式:哲學,愛情- List<Author> authors = getAuthors();authors.stream().flatMap(author -> author.getBookList().stream()).distinct().flatMap(book -> Arrays.stream(book.getCategory().split(","))).distinct().filter(category -> "哲學".equals(category) || "愛情".equals(category) ? false : true).forEach(category -> System.out.println(category));
3.4.3 終結操作
-
forEach
- 對流中的元素進行遍歷操作,我們通過傳入的參數去指定對遍歷到的元素進行什么具體操作。
- 例子:輸出所有作家的名字
- List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).distinct().forEach(name -> System.out.println(name));
-
count
- 可以用來獲取當前流中元素的個數。
- 例子:
打印這些作家的所出書籍的數目,注意刪除重復元素。- List<Author> authors = getAuthors();long count = authors.stream().flatMap(author -> author.getBookList().stream()).distinct().count();System.out.println(count);
-
max & min
- 可以用來或者流中的最值。
- 例子:
分別獲取這些作家的所出書籍的最高分和最低分并打印。- // 分別獲取這些作家的所出書籍的最高分和最低分并打印。List<Author> authors = getAuthors();Optional<Integer> max = authors.stream().flatMap(author -> author.getBookList().stream()).map(book -> book.getScore()).max((score1, score2) -> { return score1 - score2; });Optional<Integer> min = authors.stream().flatMap(author -> author.getBookList().stream()).map(book -> book.getScore()).min((score1, score2) -> { return score1 - score2; });System.out.println(max.get());System.out.println(min.get());
-
collectr
- 把當前流轉換成一個集合。
- 例子:
- 獲取一個存放所有作者名字的List集合。
- // 獲取一個存放所有作者名字的List集合。List<Author> authors = getAuthors();List<String> list = authors.stream().distinct() // 去重.map(author -> author.getName()) // 獲得所有作家的名字.collect(Collectors.toList()); // 轉換成ListSystem.out.println(list);
- 獲取一個所有書名的Set集合。
- // 獲取一個所有書名的Set集合。List<Author> authors = getAuthors();Set<Book> set = authors.stream().flatMap(author -> author.getBookList().stream()).collect(Collectors.toSet());// 轉換成Set 集合System.out.println(set);
- 獲取一個map集合,map的key為作者名,value為List
- // 獲取一個map集合,map的key為作者名,value為List<Book>List<Author> authors = getAuthors();Map<String, List<Book>> collect = authors.stream().distinct() // 去重.collect(Collectors.toMap(author -> author.getName(), author -> author.getBookList())); // 轉換成Map 集合System.out.println(collect);
-
查找與匹配
-
anyMatch
- 可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
- 例子:
判斷是否有年齡在17以上的作家 - List<Author> authors = getAuthors();boolean match = authors.stream().anyMatch(author -> author.getAge() > 17);System.out.println(match);
-
allMatch
- 可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
- 例子:
判斷是否所有的作家都是成年人 - List<Author> authors = getAuthors();boolean match = authors.stream().allMatch(author -> author.getAge() >= 18);System.out.println(match);
-
noneMatch
- 可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
- 例子:
判斷作家是否都沒有超過100歲的。 - List<Author> authors = getAuthors();boolean match = authors.stream().noneMatch(author -> author.getAge() > 100);System.out.println(match);
-
findAny
- 獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。
- 例子:
獲取任意一個年齡大于18的作家,如果存在就輸出他的名字 - List<Author> authors = getAuthors();Optional<Author> optionalAuthor = authors.stream().filter(author -> author.getAge() > 18).findAny();optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
-
findFirst
- 獲取流中的第一個元素。
- 例子:
獲取一個年齡最小的作家,并且輸出他的名字 - List<Author> authors = getAuthors();Optional<Author> optionalAuthor = authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
-
-
reduce 歸并
- 對流中的數據按照你指定的計算方式計算出一個結果。
- reduce的作用是把stream中的元素給組合起來,我們可以傳入一個初始值,它會按照我們的計算方式依次拿流中的元素和初始化值 的基礎上進行計算,計算結果再和后面的元素計算。
- reduce 兩個參數內部的重載形式 內部的計算方式如下:
- T result = identity;for(T element this stream)result = accumulator .apply(result, element)return result;
- 其中identity就是我們可以通過方法參數傳入的初始值,accumulator的apply具體進行什么計算也是我們通過方法參數來確定的。
- 例子
- 使用reduce求所有作者年齡的和
- List<Author> authors = getAuthors(); Integer sum = authors.stream().distinct().map(author -> author.getAge()).reduce(0, (result, element) -> result + element); System.out.println(sum);
- 使用reduce求所有作者中年齡的最大值
- List<Author> authors = getAuthors(); Integer max = authors.stream().distinct().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result); System.out.println(max);
- 使用reduce求所有作者中年齡的最小值
- List<Author> authors = getAuthors(); Integer min = authors.stream().distinct().map(author -> author.getAge()).reduce(Integer.MAX_VALUE, (result, element) -> result < element ? result : element); System.out.println(min);
- 使用reduce求所有作者年齡的和
- reduce 一個參數的重載形式內部的計算
- boolean foundAny = false;T result = null;for (T element : this stream) {if (!foundAny) {foundAny = true;result = element;}elseresult = accumulator.apply(result, element);}return foundAny ? Optional.of(result) : Optional.empty();
- 如果用 reduce 一個參數重載形式,求所有作家年齡的最小值和最大值代碼如下
- List<Author> authors = getAuthors(); // 使用reduce求所有作者中年齡的最小值 Optional<Integer> minOptional = authors.stream().distinct().map(author -> author.getAge()).reduce((result, element) -> result < element ? result : element); minOptional.ifPresent(age -> System.out.println(age)); // 使用reduce求所有作者中年齡的最大值 Optional<Integer> maxOptional = authors.stream().distinct().map(author -> author.getAge()).reduce((result, element) -> result > element ? result : element); maxOptional.ifPresent(age -> System.out.println(age));
3.5 注意事項
- 惰性求值(如果沒有終結操作沒有中間操作是不會得到執行的)
- 流是一次性的(一旦一個流對象經過終結操作之后,這個流對象就不能再被使用)
- 如果再次被使用回報以下異常
- Exception in thread "main〞 java. 1ang. IllegalStateException: stream has already been operated upon or closed
- 不會影響元數據(我們在流中可以多數據做很多處理。但是正常情況下是不會影響原來集合中的元素的。這往往也是我們期望的)
- 示例代碼驗證
- 正常情況下,不會影響原來元素如下:
- List<Author> authors = getAuthors();// 正常情況下,不會影響原來元素如下:authors.stream().map(author -> author.getAge()).map(age -> age += 10).forEach(age -> System.out.println(age));authors.forEach(author -> System.out.println(author.getAge()));
- 非正常情況下會影響原來的元素如下:
- List<Author> authors = getAuthors();// 非正常情況下會影響原來的元素如下:authors.stream().map(author -> {author.setAge(author.getAge() + 10);return author;}).forEach(author -> System.out.println(author.getAge()));authors.forEach(author -> System.out.println(author.getAge()));
- 只要不主動修改元素的堆棧地址,就不會改變原數據
- 示例代碼驗證
總結
以上是生活随笔為你收集整理的函数式编程[Lambda 表达式,Optional,Stream流]从入门到精通(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2招PDF去密码,秒学秒懂秒用上!
- 下一篇: Mac OS使用技巧之二:修改变量Pat