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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java8新特性函数式编程

發(fā)布時間:2023/12/9 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java8新特性函数式编程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、函數(shù)式編程-Stream流

Stream API ?新添加的Stream API(java.util.stream) 把真正的函數(shù)式編程風(fēng)格引入到Java中。它可以被用來對集合或者數(shù)組進(jìn)行鏈狀流式操作。可以方便的讓我們的對集合或者數(shù)組操作;

1.為什么學(xué)函數(shù)式編程

  • 能夠看懂公司里的代碼
  • 大數(shù)量下處理集合效率高
  • 代碼可讀性高
  • 消滅嵌套地獄
// 查詢未成年作家的評分在70以上的書籍 由于洋流影響所以作家和書籍可能出現(xiàn)重復(fù),需要進(jìn)行去重 List<Book> bookList = new ArrayList<>(); Set<Book> uniqueBookValues = new HashSet<>(); Set<Author> uniqueAuthorValues = new HashSet<>(); for (Author author : authors) {if (uniqueAuthorValues.add(author)) {if (author.getAge() < 18) {List<Book> books = author.getBooks();for (Book book : books) {if (book.getScore() > 70) {if (uniqueBookValues.add(book)) {bookList.add(book);}}}}} } System.out.println(bookList); List<Book> collect = authors.stream().distinct().filter(author -> author.getAge() < 18).map(author -> author.getBooks()).flatMap(Collection::stream).filter(book -> book.getScore() > 70).distinct().collect(Collectors.toList()); System.out.println(collect);

2. 函數(shù)式編程思想

? 面向?qū)ο笏枷胄枰P(guān)注用什么對象完成什么事情。而函數(shù)式編程思想就類似于我們數(shù)學(xué)中的函數(shù)。它主要關(guān)注的是對數(shù)據(jù)進(jìn)行了什么操作。

優(yōu)點(diǎn)

  • 代碼簡潔,開發(fā)快速
  • 接近自然語言,易于理解
  • 易于"并發(fā)編程"

二、Lambda表達(dá)式

?Lambda是JDK8中一個語法糖,可以允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞到方法中),使用 Lambda 表達(dá)式可以使代碼變的更加簡潔緊湊。它可以對某些匿名內(nèi)部類的寫法進(jìn)行簡化。它是函數(shù)式編程思想的一個重要體現(xiàn)。讓我們不用關(guān)注是什么對象。而是更關(guān)注我們對數(shù)據(jù)進(jìn)行了什么操作。

1. 核心原則

可推導(dǎo)可省略

2.基本格式

(參數(shù)列表)->{代碼}(parameters) -> expression 或 (parameters) ->{ statements; }

3.舉例說明

例一:
我們在創(chuàng)建線程并啟動時可以使用匿名內(nèi)部類的寫法:

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("你知道嗎 我比你想象的 更想在你身邊");} }).start();

可以使用Lambda的格式對其進(jìn)行修改。修改后如下:

new Thread(()->{System.out.println("你知道嗎 我比你想象的 更想在你身邊"); }).start();

例二:

現(xiàn)有方法定義如下,其中IntBinaryOperator是一個接口。先使用匿名內(nèi)部類的寫法調(diào)用該方法。

public static int calculateNum(IntBinaryOperator operator){int a = 10;int b = 20;return operator.applyAsInt(a, b);}public static void main(String[] args) {int i = calculateNum(new IntBinaryOperator() {@Overridepublic int applyAsInt(int left, int right) {return left + right;}});System.out.println(i);}

Lambda寫法:

public static void main(String[] args) {int i = calculateNum((int left, int right)->{return left + right;});System.out.println(i);}

例三:

現(xiàn)有方法定義如下,其中IntPredicate是一個接口。先使用匿名內(nèi)部類的寫法調(diào)用該方法。

public static void printNum(IntPredicate predicate){int[] arr = {1,2,3,4,5,6,7,8,9,10};for (int i : arr) {if(predicate.test(i)){System.out.println(i);}}}public static void main(String[] args) {printNum(new IntPredicate() {@Overridepublic boolean test(int value) {return value%2==0;}});}

Lambda寫法:

public static void main(String[] args) {printNum((int value)-> {return value%2==0;});}public static void printNum(IntPredicate predicate){int[] arr = {1,2,3,4,5,6,7,8,9,10};for (int i : arr) {if(predicate.test(i)){System.out.println(i);}}}

例四:

現(xiàn)有方法定義如下,其中Function是一個接口。先使用匿名內(nèi)部類的寫法調(diào)用該方法。

public static <R> R typeConver(Function<String,R> function){String str = "1235";R result = function.apply(str);return result;}public static void main(String[] args) {Integer result = typeConver(new Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.valueOf(s);}});System.out.println(result);}

Lambda寫法:

Integer result = typeConver((String s)->{return Integer.valueOf(s);});System.out.println(result);

例五:

現(xiàn)有方法定義如下,其中IntConsumer是一個接口。先使用匿名內(nèi)部類的寫法調(diào)用該方法。

public static void foreachArr(IntConsumer consumer){int[] arr = {1,2,3,4,5,6,7,8,9,10};for (int i : arr) {consumer.accept(i);}}public static void main(String[] args) {foreachArr(new IntConsumer() {@Overridepublic void accept(int value) {System.out.println(value);}});}

Lambda寫法:

public static void main(String[] args) {foreachArr((int value)->{System.out.println(value);});}

4. 省略規(guī)則與特征

省略規(guī)則

  • 參數(shù)類型可以省略
  • 方法體只有一句代碼時大括號return和唯一一句代碼的分號可以省略
  • 方法只有一個參數(shù)時小括號可以省略
  • 以上這些規(guī)則都記不住也可以省略不記

Lambda表達(dá)式的重要特征:

  • 可選類型聲明:不需要聲明參數(shù)類型,編譯器可以統(tǒng)一識別參數(shù)值。
  • 可選的參數(shù)圓括號:一個參數(shù)無需定義圓括號,但多個參數(shù)需要定義圓括號。
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  • 可選的返回關(guān)鍵字:如果主體只有一個表達(dá)式返回值則編譯器會自動返回值,大括號需要指定表達(dá)式返回了一個數(shù)值。

三、Stream流

?Java 8 的Stream使用的是函數(shù)式編程模式,如同它的名字一樣,它可以被用來對集合或數(shù)組進(jìn)行鏈狀流式的操作。可以更方便的讓我們對集合或數(shù)組操作。

Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。Stream 使用一種類似用 SQL 語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對 Java 集合運(yùn)算和表達(dá)的高階抽象。Stream API可以極大提高Java程序員的生產(chǎn)力,讓程序員寫出高效率、干凈、簡潔的代碼。這種風(fēng)格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理, 比如篩選, 排序,聚合等。元素流在管道中經(jīng)過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結(jié)果。

1. 案例數(shù)據(jù)準(zhǔn)備

<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency></dependencies> @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode//用于后期的去重使用 public class Author {//idprivate Long id;//姓名private String name;//年齡private Integer age;//簡介private String intro;//作品private List<Book> books; } @Data @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode//用于后期的去重使用 public class Book {//idprivate Long id;//書名private String name;//分類private String category;//評分private Integer score;//簡介private String intro;} private static List<Author> getAuthors() {//數(shù)據(jù)初始化Author author = new Author(1L,"蒙多",33,"一個從菜刀中明悟哲理的祖安人",null);Author author2 = new Author(2L,"亞拉索",15,"狂風(fēng)也追逐不上他的思考速度",null);Author author3 = new Author(3L,"易",14,"是這個世界在限制他的思維",null);Author author4 = new Author(3L,"易",14,"是這個世界在限制他的思維",null);//書籍列表List<Book> books1 = new ArrayList<>();List<Book> books2 = new ArrayList<>();List<Book> books3 = new ArrayList<>();books1.add(new Book(1L,"刀的兩側(cè)是光明與黑暗","哲學(xué),愛情",88,"用一把刀劃分了愛恨"));books1.add(new Book(2L,"一個人不能死在同一把刀下","個人成長,愛情",99,"講述如何從失敗中明悟真理"));books2.add(new Book(3L,"那風(fēng)吹不到的地方","哲學(xué)",85,"帶你用思維去領(lǐng)略世界的盡頭"));books2.add(new Book(3L,"那風(fēng)吹不到的地方","哲學(xué)",85,"帶你用思維去領(lǐng)略世界的盡頭"));books2.add(new Book(4L,"吹或不吹","愛情,個人傳記",56,"一個哲學(xué)家的戀愛觀注定很難把他所在的時代理解"));books3.add(new Book(5L,"你的劍就是我的劍","愛情",56,"無法想象一個武者能對他的伴侶這么的寬容"));books3.add(new Book(6L,"風(fēng)與劍","個人傳記",100,"兩個哲學(xué)家靈魂和肉體的碰撞會激起怎么樣的火花呢?"));books3.add(new Book(6L,"風(fēng)與劍","個人傳記",100,"兩個哲學(xué)家靈魂和肉體的碰撞會激起怎么樣的火花呢?"));author.setBooks(books1);author2.setBooks(books2);author3.setBooks(books3);author4.setBooks(books3);List<Author> authorList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));return authorList;}

2. 案例快速入門

需求
?我們可以調(diào)用getAuthors方法獲取到作家的集合。現(xiàn)在需要打印所有年齡小于18的作家的名字,并且要注意去重。

實(shí)現(xiàn)

//打印所有年齡小于18的作家的名字,并且要注意去重List<Author> authors = getAuthors();authors.stream()//把集合轉(zhuǎn)換成流.distinct()//先去除重復(fù)的作家.filter(author -> author.getAge()<18)//篩選年齡小于18的.forEach(author -> System.out.println(author.getName()));//遍歷打印名字

3. 常用操作

1. 創(chuàng)建流

單列集合: 集合對象.stream()

List<Author> authors = getAuthors();Stream<Author> stream = authors.stream();

數(shù)組:Arrays.stream(數(shù)組) 或者使用Stream.of來創(chuàng)建

Integer[] arr = {1,2,3,4,5};Stream<Integer> stream = Arrays.stream(arr);Stream<Integer> stream2 = Stream.of(arr);

雙列集合:轉(zhuǎn)換成單列集合后再創(chuàng)建

Map<String,Integer> map = new HashMap<>();map.put("蠟筆小新",19);map.put("黑子",17);map.put("日向翔陽",16);Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

2.中間操作

filter

?可以對流中的元素進(jìn)行條件過濾,符合過濾條件的才能繼續(xù)留在流中。

例如:

?打印所有姓名長度大于1的作家的姓名

List<Author> authors = getAuthors();authors.stream().filter(author -> author.getName().length()>1).forEach(author -> System.out.println(author.getName()));

map

?可以把對流中的元素進(jìn)行計算或轉(zhuǎn)換。

例如

?打印所有作家的姓名

List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).forEach(name->System.out.println(name)); //打印所有作家的姓名List<Author> authors = getAuthors();authors.stream().map(author -> author.getAge()).map(age->age+10).forEach(age-> System.out.println(age));

distinct

?可以去除流中的重復(fù)元素。

例如:

?打印所有作家的姓名,并且要求其中不能有重復(fù)元素。

List<Author> authors = getAuthors();authors.stream().distinct().forEach(author -> System.out.println(author.getName()));

注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重寫equals方法。

sorted

?可以對流中的元素進(jìn)行排序。

例如:

?對流中的元素按照年齡進(jìn)行降序排序,并且要求不能有重復(fù)的元素。

List<Author> authors = getAuthors(); // 對流中的元素按照年齡進(jìn)行降序排序,并且要求不能有重復(fù)的元素。authors.stream().distinct().sorted().forEach(author -> System.out.println(author.getAge())); List<Author> authors = getAuthors(); // 對流中的元素按照年齡進(jìn)行降序排序,并且要求不能有重復(fù)的元素。authors.stream().distinct().sorted((o1, o2) -> o2.getAge()-o1.getAge()).forEach(author -> System.out.println(author.getAge()));

注意:如果調(diào)用空參的sorted()方法,需要流中的元素是實(shí)現(xiàn)了Comparable。

limit

?可以設(shè)置流的最大長度,超出的部分將被拋棄。

例如:

?對流中的元素按照年齡進(jìn)行降序排序,并且要求不能有重復(fù)的元素,然后打印其中年齡最大的兩個作家的姓名。

List<Author> authors = getAuthors();authors.stream().distinct().sorted().limit(2).forEach(author -> System.out.println(author.getName()));

skip

?跳過流中的前n個元素,返回剩下的元素

例如:

?打印除了年齡最大的作家外的其他作家,要求不能有重復(fù)元素,并且按照年齡降序排序。

// 打印除了年齡最大的作家外的其他作家,要求不能有重復(fù)元素,并且按照年齡降序排序。List<Author> authors = getAuthors();authors.stream().distinct().sorted().skip(1).forEach(author -> System.out.println(author.getName()));

flatMap

?map只能把一個對象轉(zhuǎn)換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉(zhuǎn)換成多個對象作為流中的元素。

例一:

?打印所有書籍的名字。要求對重復(fù)的元素進(jìn)行去重。

// 打印所有書籍的名字。要求對重復(fù)的元素進(jìn)行去重。List<Author> authors = getAuthors();authors.stream().flatMap(author -> author.getBooks().stream()).distinct().forEach(book -> System.out.println(book.getName()));

例二:

?打印現(xiàn)有數(shù)據(jù)的所有分類。要求對分類進(jìn)行去重。不能出現(xiàn)這種格式:哲學(xué),愛情

// 打印現(xiàn)有數(shù)據(jù)的所有分類。要求對分類進(jìn)行去重。不能出現(xiàn)這種格式:哲學(xué),愛情 愛情List<Author> authors = getAuthors();authors.stream().flatMap(author -> author.getBooks().stream()).distinct().flatMap(book -> Arrays.stream(book.getCategory().split(","))).distinct().forEach(category-> System.out.println(category));

3. 終結(jié)操作

forEach

?對流中的元素進(jìn)行遍歷操作,我們通過傳入的參數(shù)去指定對遍歷到的元素進(jìn)行什么具體操作。

例子:
?輸出所有作家的名字

// 輸出所有作家的名字List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).distinct().forEach(name-> System.out.println(name));

count

?可以用來獲取當(dāng)前流中元素的個數(shù)。

例子:
?打印這些作家的所出書籍的數(shù)目,注意刪除重復(fù)元素。

// 打印這些作家的所出書籍的數(shù)目,注意刪除重復(fù)元素。List<Author> authors = getAuthors();long count = authors.stream().flatMap(author -> author.getBooks().stream()).distinct().count();System.out.println(count);

max&min

?可以用來或者流中的最值。

例子:

?分別獲取這些作家的所出書籍的最高分和最低分并打印。

// 分別獲取這些作家的所出書籍的最高分和最低分并打印。//Stream<Author> -> Stream<Book> ->Stream<Integer> ->求值List<Author> authors = getAuthors();Optional<Integer> max = authors.stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).max((score1, score2) -> score1 - score2);Optional<Integer> min = authors.stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).min((score1, score2) -> score1 - score2);System.out.println(max.get());System.out.println(min.get());

collect

?把當(dāng)前流轉(zhuǎn)換成一個集合。

例子:

?獲取一個存放所有作者名字的List集合。

// 獲取一個存放所有作者名字的List集合。List<Author> authors = getAuthors();List<String> nameList = authors.stream().map(author -> author.getName()).collect(Collectors.toList());System.out.println(nameList);

?獲取一個所有書名的Set集合。

// 獲取一個所有書名的Set集合。List<Author> authors = getAuthors();Set<Book> books = authors.stream().flatMap(author -> author.getBooks().stream()).collect(Collectors.toSet());System.out.println(books);

? 獲取一個Map集合,map的key為作者名,value為List

// 獲取一個Map集合,map的key為作者名,value為List<Book>List<Author> authors = getAuthors();Map<String, List<Book>> map = authors.stream().distinct().collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));System.out.println(map);

查找與匹配

anyMatch

可以用來判斷是否有任意符合匹配條件的元素,結(jié)果為boolean類型。

例子:

?判斷是否有年齡在29以上的作家

// 判斷是否有年齡在29以上的作家List<Author> authors = getAuthors();boolean flag = authors.stream().anyMatch(author -> author.getAge() > 29);System.out.println(flag);
allMatch

?可以用來判斷是否都符合匹配條件,結(jié)果為boolean類型。如果都符合結(jié)果為true,否則結(jié)果為false。

例子:

?判斷是否所有的作家都是成年人

// 判斷是否所有的作家都是成年人List<Author> authors = getAuthors();boolean flag = authors.stream().allMatch(author -> author.getAge() >= 18);System.out.println(flag);
noneMatch

?可以判斷流中的元素是否都不符合匹配條件。如果都不符合結(jié)果為true,否則結(jié)果為false

例子:

?判斷作家是否都沒有超過100歲的。

// 判斷作家是否都沒有超過100歲的。List<Author> authors = getAuthors();boolean b = authors.stream().noneMatch(author -> author.getAge() > 100);System.out.println(b);
findAny

?獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。

例子:

?獲取任意一個年齡大于18的作家,如果存在就輸出他的名字

// 獲取任意一個年齡大于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> first = authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();first.ifPresent(author -> System.out.println(author.getName()));

reduce歸并

?對流中的數(shù)據(jù)按照你指定的計算方式計算出一個結(jié)果。(縮減操作)

? reduce的作用是把stream中的元素給組合起來,我們可以傳入一個初始值,它會按照我們的計算方式依次拿流中的元素和初始化值進(jìn)行計算,計算結(jié)果再和后面的元素計算。

? reduce兩個參數(shù)的重載形式內(nèi)部的計算方式如下:

T result = identity; for (T element : this stream)result = accumulator.apply(result, element) return result;

?其中identity就是我們可以通過方法參數(shù)傳入的初始值,accumulator的apply具體進(jìn)行什么計算也是我們通過方法參數(shù)來確定的。

例子:

?使用reduce求所有作者年齡的和

// 使用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求所有作者中年齡的最大值

// 使用reduce求所有作者中年齡的最大值List<Author> authors = getAuthors();Integer max = authors.stream().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);System.out.println(max);

? 使用reduce求所有作者中年齡的最小值

// 使用reduce求所有作者中年齡的最小值List<Author> authors = getAuthors();Integer min = authors.stream().map(author -> author.getAge()).reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);System.out.println(min);

?reduce一個參數(shù)的重載形式內(nèi)部的計算

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

? 如果用一個參數(shù)的重載方法去求最小值代碼如下:

// 使用reduce求所有作者中年齡的最小值List<Author> authors = getAuthors();Optional<Integer> minOptional = authors.stream().map(author -> author.getAge()).reduce((result, element) -> result > element ? element : result);minOptional.ifPresent(age-> System.out.println(age));

4. 注意事項(xiàng)

  • 惰性求值(如果沒有終結(jié)操作,沒有中間操作是不會得到執(zhí)行的)
  • 流是一次性的(一旦一個流對象經(jīng)過一個終結(jié)操作后。這個流就不能再被使用)
  • 不會影響原數(shù)據(jù)(我們在流中可以多數(shù)據(jù)做很多處理。但是正常情況下是不會影響原來集合中的元素的。這往往也是我們期望的)

四、Optional

?我們在編寫代碼的時候出現(xiàn)最多的就是空指針異常。所以在很多情況下我們需要做各種非空的判斷。

?例如:

Author author = getAuthor();if(author!=null){System.out.println(author.getName());}

?尤其是對象中的屬性還是一個對象的情況下。這種判斷會更多。 而過多的判斷語句會讓我們的代碼顯得臃腫不堪。所以在JDK8中引入了Optional,養(yǎng)成使用Optional的習(xí)慣后你可以寫出更優(yōu)雅的代碼來避免空指針異常。并且在很多函數(shù)式編程相關(guān)的API中也都用到了Optional,如果不會使用Optional也會對函數(shù)式編程的學(xué)習(xí)造成影響。

1. 創(chuàng)建對象

?Optional就好像是包裝類,可以把我們的具體數(shù)據(jù)封裝Optional對象內(nèi)部。然后我們?nèi)ナ褂肙ptional中封裝好的方法操作封裝進(jìn)去的數(shù)據(jù)就可以非常優(yōu)雅的避免空指針異常。

?我們一般使用Optional靜態(tài)方法ofNullable來把數(shù)據(jù)封裝成一個Optional對象。無論傳入的參數(shù)是否為null都不會出現(xiàn)問題。

Author author = getAuthor();Optional<Author> authorOptional = Optional.ofNullable(author);

?你可能會覺得還要加一行代碼來封裝數(shù)據(jù)比較麻煩。但是如果改造下getAuthor方法,讓其的返回值就是封裝好的Optional的話,我們在使用時就會方便很多。

?而且在實(shí)際開發(fā)中我們的數(shù)據(jù)很多是從數(shù)據(jù)庫獲取的。Mybatis從3.5版本可以也已經(jīng)支持Optional了。我們可以直接把dao方法的返回值類型定義成Optional類型,MyBastis會自己把數(shù)據(jù)封裝成Optional對象返回。封裝的過程也不需要我們自己操作。

?如果你確定一個對象不是空的則可以使用Optional靜態(tài)方法of來把數(shù)據(jù)封裝成Optional對象。

Author author = new Author();Optional<Author> authorOptional = Optional.of(author);

?但是一定要注意,如果使用of的時候傳入的參數(shù)必須不為null。(嘗試下傳入null會出現(xiàn)什么結(jié)果)

?如果一個方法的返回值類型是Optional類型。而如果我們經(jīng)判斷發(fā)現(xiàn)某次計算得到的返回值為null,這個時候就需要把null封裝成Optional對象返回。這時則可以使用Optional靜態(tài)方法empty來進(jìn)行封裝。

Optional.empty()

?所以最后你覺得哪種方式會更方便呢?ofNullable

2. 安全消費(fèi)值

?我們獲取到一個Optional對象后肯定需要對其中的數(shù)據(jù)進(jìn)行使用。這時候我們可以使用其ifPresent方法對來消費(fèi)其中的值。
?這個方法會判斷其內(nèi)封裝的數(shù)據(jù)是否為空,不為空時才會執(zhí)行具體的消費(fèi)代碼。這樣使用起來就更加安全了。?例如,以下寫法就優(yōu)雅的避免了空指針異常。

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.ifPresent(author -> System.out.println(author.getName()));

3. 獲取值

?如果我們想獲取值自己進(jìn)行處理可以使用get方法獲取,但是不推薦。因?yàn)楫?dāng)Optional內(nèi)部的數(shù)據(jù)為空的時候會出現(xiàn)異常。

4.安全獲取值

?如果我們期望安全的獲取值。我們不推薦使用get方法,而是使用Optional提供的以下方法。

  • orElseGet

    獲取數(shù)據(jù)并且設(shè)置數(shù)據(jù)為空時的默認(rèn)值。如果數(shù)據(jù)不為空就能獲取到該數(shù)據(jù)。如果為空則根據(jù)你傳入的參數(shù)來創(chuàng)建對象作為默認(rèn)值返回。

    Optional<Author> authorOptional = Optional.ofNullable(getAuthor());Author author1 = authorOptional.orElseGet(() -> new Author());
  • orElseThrow

    獲取數(shù)據(jù),如果數(shù)據(jù)不為空就能獲取到該數(shù)據(jù)。如果為空則根據(jù)你傳入的參數(shù)來創(chuàng)建異常拋出。

    Optional<Author> authorOptional = Optional.ofNullable(getAuthor());try {Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author為空"));System.out.println(author.getName());} catch (Throwable throwable) {throwable.printStackTrace();}

5. 過濾

? 我們可以使用filter方法對數(shù)據(jù)進(jìn)行過濾。如果原本是有數(shù)據(jù)的,但是不符合判斷,也會變成一個無數(shù)據(jù)的Optional對象。

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

6. 判斷

?我們可以使用isPresent方法進(jìn)行是否存在數(shù)據(jù)的判斷。如果為空返回值為false,如果不為空,返回值為true。但是這種方式并不能體現(xiàn)Optional的好處,更推薦使用ifPresent方法

Optional<Author> authorOptional = Optional.ofNullable(getAuthor());if (authorOptional.isPresent()) {System.out.println(authorOptional.get().getName());}

7. 數(shù)據(jù)轉(zhuǎn)換

?Optional還提供了map可以讓我們的對數(shù)據(jù)進(jìn)行轉(zhuǎn)換,并且轉(zhuǎn)換得到的數(shù)據(jù)也還是被Optional包裝好的,保證了我們的使用安全。

例如我們想獲取作家的書籍集合。

private static void testMap() {Optional<Author> authorOptional = getAuthorOptional();Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());optionalBooks.ifPresent(books -> System.out.println(books));}

五、函數(shù)式接口

?只有一個抽象方法的接口我們稱之為函數(shù)接口。

?JDK的函數(shù)式接口都加上了@FunctionalInterface 注解進(jìn)行標(biāo)識。但是無論是否加上該注解只要接口中只有一個抽象方法,都是函數(shù)式接口。

2.常見函數(shù)式接口

  • ? Consumer 消費(fèi)接口

    根據(jù)其中抽象方法的參數(shù)列表和返回值類型知道,我們可以在方法中對傳入的參數(shù)進(jìn)行消費(fèi)。

  • ? Function 計算轉(zhuǎn)換接口

    根據(jù)其中抽象方法的參數(shù)列表和返回值類型知道,我們可以在方法中對傳入的參數(shù)計算或轉(zhuǎn)換,把結(jié)果返回

  • ? Predicate 判斷接口

    根據(jù)其中抽象方法的參數(shù)列表和返回值類型知道,我們可以在方法中對傳入的參數(shù)條件判斷,返回判斷結(jié)果

  • ? Supplier 生產(chǎn)型接口

    根據(jù)其中抽象方法的參數(shù)列表和返回值類型知道,我們可以在方法中創(chuàng)建對象,把創(chuàng)建好的對象返回

3。 常用的默認(rèn)方法

  • and

    我們在使用Predicate接口時候可能需要進(jìn)行判斷條件的拼接。而and方法相當(dāng)于是使用&&來拼接兩個判斷條件

    例如:

    打印作家中年齡大于17并且姓名的長度大于1的作家。

    List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.and(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getName().length()>1;}})).forEach(author -> System.out.println(author));
  • or

    我們在使用Predicate接口時候可能需要進(jìn)行判斷條件的拼接。而or方法相當(dāng)于是使用||來拼接兩個判斷條件。

    例如:

    打印作家中年齡大于17或者姓名的長度小于2的作家。

    // 打印作家中年齡大于17或者姓名的長度小于2的作家。List<Author> authors = getAuthors();authors.stream().filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.or(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getName().length()<2;}})).forEach(author -> System.out.println(author.getName()));
  • negate

    Predicate接口中的方法。negate方法相當(dāng)于是在判斷添加前面加了個! 表示取反

    例如:

    打印作家中年齡不大于17的作家。

    // 打印作家中年齡不大于17的作家。List<Author> authors = getAuthors();authors.stream().filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.negate()).forEach(author -> System.out.println(author.getAge()));

六、方法引用

?我們在使用lambda時,如果方法體中只有一個方法的調(diào)用的話(包括構(gòu)造方法),我們可以用方法引用進(jìn)一步簡化代碼。

1. 推薦用法

?我們在使用lambda時不需要考慮什么時候用方法引用,用哪種方法引用,方法引用的格式是什么。我們只需要在寫完lambda方法發(fā)現(xiàn)方法體只有一行代碼,并且是方法的調(diào)用時使用快捷鍵嘗試是否能夠轉(zhuǎn)換成方法引用即可。

?當(dāng)我們方法引用使用的多了慢慢的也可以直接寫出方法引用。

2. 基本格式

類名或者對象名::方法名

3. 語法詳解(了解)

3.1 引用類的靜態(tài)方法

其實(shí)就是引用類的靜態(tài)方法

格式

類名::方法名

使用前提

?如果我們在重寫方法的時候,方法體中只有一行代碼,并且這行代碼是調(diào)用了某個類的靜態(tài)方法,并且我們把要重寫的抽象方法中所有的參數(shù)都按照順序傳入了這個靜態(tài)方法中,這個時候我們就可以引用類的靜態(tài)方法。

例如:
如下代碼就可以用方法引用進(jìn)行簡化

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(age->String.valueOf(age));

注意,如果我們所重寫的方法是沒有參數(shù)的,調(diào)用的方法也是沒有參數(shù)的也相當(dāng)于符合以上規(guī)則。

優(yōu)化后如下:

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(String::valueOf);

3.2 引用對象的實(shí)例方法

格式

對象名::方法名

使用前提

?如果我們在重寫方法的時候,方法體中只有一行代碼,并且這行代碼是調(diào)用了某個對象的成員方法,并且我們把要重寫的抽象方法中所有的參數(shù)都按照順序傳入了這個成員方法中,這個時候我們就可以引用對象的實(shí)例方法

例如:

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();StringBuilder sb = new StringBuilder();authorStream.map(author -> author.getName()).forEach(name->sb.append(name));

優(yōu)化后:

List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();StringBuilder sb = new StringBuilder();authorStream.map(author -> author.getName()).forEach(sb::append);

3.4 引用類的實(shí)例方法

格式

類名::方法名

使用前提

?如果我們在重寫方法的時候,方法體中只有一行代碼,并且這行代碼是調(diào)用了第一個參數(shù)的成員方法,并且我們把要重寫的抽象方法中剩余的所有的參數(shù)都按照順序傳入了這個成員方法中,這個時候我們就可以引用類的實(shí)例方法。

例如:

interface UseString{String use(String str,int start,int length);}public static String subAuthorName(String str, UseString useString){int start = 0;int length = 1;return useString.use(str,start,length);}public static void main(String[] args) {subAuthorName("三更草堂", new UseString() {@Overridepublic String use(String str, int start, int length) {return str.substring(start,length);}});}

優(yōu)化后如下:

public static void main(String[] args) {subAuthorName("三更草堂", String::substring);}

3.5 構(gòu)造器引用

?如果方法體中的一行代碼是構(gòu)造器的話就可以使用構(gòu)造器引用。

格式

類名::new

使用前提

?如果我們在重寫方法的時候,方法體中只有一行代碼,并且這行代碼是調(diào)用了某個類的構(gòu)造方法,并且我們把要重寫的抽象方法中的所有的參數(shù)都按照順序傳入了這個構(gòu)造方法中,這個時候我們就可以引用構(gòu)造器。

例如:

List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).map(name->new StringBuilder(name)).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

優(yōu)化后:

List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).map(StringBuilder::new).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

七、高級用法

基本數(shù)據(jù)類型優(yōu)化

?我們之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的參數(shù)和返回值都是引用數(shù)據(jù)類型。

?即使我們操作的是整數(shù)小數(shù),但是實(shí)際用的都是他們的包裝類。JDK5中引入的自動裝箱和自動拆箱讓我們在使用對應(yīng)的包裝類時就好像使用基本數(shù)據(jù)類型一樣方便。但是你一定要知道裝箱和拆箱肯定是要消耗時間的。雖然這個時間消耗很下。但是在大量的數(shù)據(jù)不斷的重復(fù)裝箱拆箱的時候,你就不能無視這個時間損耗了。

?所以為了讓我們能夠?qū)@部分的時間消耗進(jìn)行優(yōu)化。Stream還提供了很多專門針對基本數(shù)據(jù)類型的方法。

? 例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

private static void test27() {List<Author> authors = getAuthors();authors.stream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);authors.stream().mapToInt(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);}

并行流

?當(dāng)流中有大量元素時,我們可以使用并行流去提高操作的效率。其實(shí)并行流就是把任務(wù)分配給多個線程去完全。如果我們自己去用代碼實(shí)現(xiàn)的話其實(shí)會非常的復(fù)雜,并且要求你對并發(fā)編程有足夠的理解和認(rèn)識。而如果我們使用Stream的話,我們只需要修改一個方法的調(diào)用就可以使用并行流來幫我們實(shí)現(xiàn),從而提高效率。

?parallel方法可以把串行流轉(zhuǎn)換成并行流。

private static void test28() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = stream.parallel().peek(new Consumer<Integer>() {@Overridepublic void accept(Integer num) {System.out.println(num+Thread.currentThread().getName());}}).filter(num -> num > 5).reduce((result, ele) -> result + ele).get();System.out.println(sum);}

? 也可以通過parallelStream直接獲取并行流對象。

List<Author> authors = getAuthors();authors.parallelStream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);

總結(jié)

以上是生活随笔為你收集整理的Java8新特性函数式编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。