函數式編程從入門到精通

1.概述

1.1為什么學?

* 能夠看懂公司里的代碼
* 大數量下處理集合效率高
* 代碼可讀性高
* 消滅嵌套地獄
    //查詢未成年作家評分在70分以上的書籍,由于流的影響所以作家和書籍可能會重復出現,所以要去重public void test1() {List<Book> bookList = new ArrayList<>();List<Author> authorList = new ArrayList<>();Set<Book> uniqueBookValues = new HashSet<>();Set<Author> uniqueAuthorValues = new HashSet<>();for (Author author : authorList) {// 這里如果重復就不會添加成功if (uniqueAuthorValues.add(author)) {if (author.getAge() < 18) {List<Book> books = author.getBookList();for (Book book : books) {if (book.getScore() > 70D) {// 如果之前有這本書就不會再次添加if (uniqueBookValues.add(book)) {bookList.add(book);}}}}}}System.out.println(bookList);// authorList.add(new Author());// 函數式寫法List<Book> collect = authorList.stream().distinct().filter(author -> author.getAge() < 18).map(author -> author.getBookList()).flatMap(Collection::stream).filter(book -> book.getScore() > 70).distinct().collect(Collectors.toList());System.out.println(collect);}

1.2函數式編程思想

1.2.1概念

面向對象思想需要關注用什么對象完成什么事情。而函數式編程思想就類似于我們數學中的函數。它主要關注的是對數據進行了什么操作。

1.2.2優點

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

2. Lambda表達式


2.1概述

Lambda是JDK8中一個語法糖。他可以對某些匿名內部類的寫法進行簡化。它是函數式編程思想的一個重要體現。讓我們不用關注是什么對象。而是更關注我們對數據進行了什么操作。

不關注類名和方法名,只關注參數(數據)和方法體(操作

2.2核心原則

可推導可省略

2.3基本格式

(參數列表)->{代碼}

        //我們在創建線程并啟動時可以使用匿名內部類的寫法:new Thread(new Runnable() {@Overridepublic void run() {System.out.println("這是匿名內部類寫法");}}).start();//可以使用Lambda的格式對其進行修改。修改后如下:new Thread(() -> {System.out.println("這是lambda寫法");}).start();

2.4省略規則

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

3.Stream流

3.1概述

Java8的Stream使用的是函數式編程模式,如同它的名字一樣,它可以被用來對集合或數組進行鏈狀流式的操作。可以更方便的我們對集合或數組操作。

3.2案例準備

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

3.3快速入門

3.3.1需求

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

3.3.2實現

List<Author> authors = getAuthors();authors.stream() //把集合轉換成流.distinct() //去重.filter(author -> author.getAge() < 18) //篩選年齡小于18的.forEach(author -> System.out.println(author.getName())); //遍歷打印名字

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);S
map.put("黑子",17);
map.put("日向翔陽",16);
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream()

3.4.2中間操作

filter

可以對流中的元素進行條件過濾,符合過濾條件的才能繼續留在流中。
例如:打印所有姓名長度大于1的作家的姓名

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

可以把對流中的元素進行計算或轉換。
例如:打印所有作家的姓名

        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

可以去除流中的重復元素。
例如:打印所有作家的姓名,并且要求其中不能有重復元素。
注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重新equals方法。

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

可以對流中的元素進行排序。
例如:對流中的元素按照年齡進行降序排序,并且要求不能有重復的元素。

注意:如果調用空參的sorted()方法,需要流中的元素是實現Comparable接口

        List<Author>authors = getAuthors();//對流中的元素按照年齡進行降序排序,并且要求不能有重復的元素authors.stream().distinct().sorted().forEach(author -> System.out.println(author.getAge()));
        List<Author>authors = getAuthors();//對流中的元素按照年齡進行降序排序,并且要求不能有重復的元素authors.stream().distinct().sorted((o1, o2) -> o2.getAge()-o1.getAge()).forEach(author -> System.out.println(author.getAge()));
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只能把一個對象轉換成另一個對象來作為流中的元素。而flatMaap可以把一個對象轉換成多個對象作為流中的元素。
例一:打印所有書籍的名字。要求對重復的元素進行去重。

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

例二:打印現有數據的所有分類。要求對分類進行去重。不能出現這種中格式:哲學,愛情

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

將流中的元素根據某個屬性或規則進行分類,并返回一個映射,其中鍵是分組依據的值,值是每個分組中元素的集合。

基本用法

例如:將作家按照名字分組,并打印出每個名字及其對應的作家。

Map<String, List<Author>> groupByName = getAuthors().stream().collect(Collectors.groupingBy(new Function<Author, String>() {@Overridepublic String apply(Author author) {return author.getName();}}));
groupByName.forEach((name, authors) -> {System.out.println(name);authors.forEach(System.out::println);
});
多級分組

有時,我們可能需要對數據進行多級分組。

例如:首先按照年齡分組,然后按照姓名分組

Map<Integer, Map<String, List<Author>>> groupByAgeThenName = getAuthors().stream().collect(Collectors.groupingBy(author -> author.getAge(), Collectors.groupingBy(author -> author.getName())));
groupByAgeThenName.forEach(new BiConsumer<Integer, Map<String, List<Author>>>() {@Overridepublic void accept(Integer integer, Map<String, List<Author>> stringListMap) {System.out.println("年齡:" + integer);stringListMap.forEach(new BiConsumer<String, List<Author>>() {@Overridepublic void accept(String name, List<Author> authors) {System.out.println("姓名:" + name);authors.forEach(System.out::println);}});}
});
分組聚合

分組操作可以與聚合操作結合使用

例如:計算每個名字的作家平均年齡

Map<String, Double> averageAgeByName = getAuthors().stream().collect(Collectors.groupingBy(author -> author.getName(), Collectors.averagingInt(author -> author.getAge())));
averageAgeByName.forEach((name, age) -> System.out.println("作者:" + name + ",平均年齡:" + age));
分組排序

分組操作還可以與排序結合使用

例如:按照年齡排序后分組

Map<Integer, List<Author>> sortedGroupByAge = getAuthors().stream().sorted(Comparator.comparing(Author::getAge)).collect(Collectors.groupingBy(Author::getAge,LinkedHashMap::new,  // 保持插入順序Collectors.toList()));
System.out.println(sortedGroupByAge);
sortedGroupByAge.forEach((age, authors) -> {System.out.println(age);authors.forEach(author -> System.out.println(author.getName()));
});

3.4.3終結操作

forEach

對流中的元素進行遍歷操作,我們通過傳入的參數去指定對遍歷到的元素進行什么具體操作。
例子:輸出所有作家的名字

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

可以用來獲取當前流中元素的個數。
例子:打印這些作家的所出書籍的數目,注意刪除重復元素。

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

可以用來獲取流中的最值。
例子:分別獲取這些作家的所出書籍的最高分和最低分并打印。

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

把當前流轉換成一個集合。
例子:

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

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

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

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

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

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

可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
例子:判斷是否有年齡在29以上的作家

        List<Author> authors = getAuthors();boolean flag = authors.stream().anyMatch(author -> author.getAge() > 29);System.out.println(flag);

allMatch

可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
例子:判斷是否所有的作家都是成年人

        List<Author> authors = getAuthors();boolean flag = authors.stream().allMatch(author -> author.getAge() > 18);System.out.println(flag);

noneMatch

可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
例子:判斷作家是否都沒有超過100歲的。

        List<Author> authors = getAuthors();boolean flag = authors.stream().noneMatch(author -> author.getAge() > 100);System.out.println(flag);
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> first = authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();first.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求所有作者中年齡的最大值

        Integer MaxAge = authors.stream().distinct().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);System.out.println(MaxAge);

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

        List<Author> authors = getAuthors();Integer MinAge = authors.stream().distinct().map(author -> author.getAge()).reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);System.out.println(MinAge);

reduce一個參數的重載形式內部的計算

boolean foundAny = false;
T result = null; 
for (T element : this stream) {    if (!foundAny) {         foundAny = true;         result = element;     }     else         result = accumulator. apply(result, element); 
} 
return foundAny ? Optional. of(result) : Optional. empty();

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

        List<Author> authors = getAuthors();Optional<Integer> MinAge = authors.stream().distinct().map(author -> author.getAge()).reduce(new BinaryOperator<Integer>() {@Overridepublic Integer apply(Integer result, Integer element) {return result > element ? element : result;}});MinAge.ifPresent(age -> System.out.println(age));

3.5注意事項

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

4. Optional

4.1概述

我們在編寫代碼的時候出現最多的就是空指針異常。所以在很多情清況下我們需要做各種非空的判斷。
例如:

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

尤其是對象中的屬性還是一個對象的情況下。這種判斷會更多。

而過多的判斷語句會讓我們的代碼顯得臃腫不堪。

所以在JDK8中引入了Optional,養成使用Optional的習慣后你可以寫出更優雅的代碼來避免空指針異常。

并且在很多函數式編程相關的API中也都用到了Optional,如果不會使用Optional也會對函數式編程的學習造成影響。

4.2使用


4.2.1創建對象

Optlonal就好像是包裝類,可以把我們的具體數據封裝Optional對象內部。然后我們去使用Optional中封裝好的方法操作封裝進去的數據就可以非常優雅的避免空指針異常。

我們一般使用Optional靜態方法ofNullable來把數據封裝成一個Opttional對象。無論傳入的參數是否為null都不會出現問題。

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

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

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

如果你確定一個對象不是空的則可以使用Optional靜態方法of來把數據封裝成Optional對象。

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

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

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

4.2.2安全消費值

我們獲取到一個Optional對象后肯定需要對其中的數據進行使用。這對候我們可以使用其ifPresent方法對來消費其中的值。

這個方法會判斷其內封裝的數據是否為空,不為空時才會執行具體的消費代碼。這樣使用起來就更加安全了。
例如,以下寫法就優雅的避免了空指針異常。

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

4.2.4安全獲取值

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

orElseGet

獲取數據并且設置數據為空時的默認值。如果數據不為空就能獲!取到該數據。如果為空則根據你傳入的參數來創建對象作為默認值返回

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

獲取數據,如果數據不為空就能獲取到該數據。如果數據為空則根據你傳入的參數來創建異常拋出

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

4.2.5過濾

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

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

4.2.6判斷

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

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

4.2.7數據轉換

Optional還提供了map可以讓我們的對數據進行轉換,并且轉換得到的數據也還是被Optional包裝好的,保證了我們的使用安全。
例如:我們想獲取作家的書籍集合。

Optional<Author> authorOptional = Optional.ofNullable (getAuthor());
Optional<List<Book>> books = authorOptional.map((author -> author.getBooks()));
books.ifPresent(new Consumer<List<Book>>() {@Overridepublic void accept(List<Book> books) {books.forEach(book -> System.out.println(book.getName()));}
});

5.函數式接口

5.1概述

只有一個抽象方法的接口我們稱之為函數式接口。
JDK的函數式接口都加上了@FunctionalInterface注解進行標識。但是無論是否加上該注解只要接口中只有一個抽象方法,都是函數式接口。

5.2常見函數式接口

Consumer消費接口

根據其中抽象方法的參數列表和返回值類型知道,我們可以在方法中對傳入的參數進行消費。

Function計算轉換接口

根據其中抽象方法的參數列表和返回值類型知道,我們可以在方法中對傳入的參數計算或轉換,把結果返回

5.3 常用的默認方法

and

我們在使用Predicate接口時候可能需要進行判斷條件的拼接。而and方法相當于是使用&&來拼接兩個判斷條件
例如:打印作家中年齡大于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));
List<Author> authors = getAuthors();
Stream<Author> authorStream = authors.stream();
authorStream.filter(author -> author.getAge() > 17 && author.getName().length() > 1).forEach(author -> System.out.println(author));

or

我們在使用Predicate接口時候可能需要進行判斷條件的拼接。而or方法相當于是使用|來拼接兩個判斷條件。
例如:打印作家中年齡大于17或者姓名的長度小于2的作家。

List<Author> authors = getAuthors();
Stream<Author> authorStream = authors.stream();
authorStream.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));

negate

Predicate接口中的方法。negate方法相當于是在判斷添加前面加了個!表示取反
例如:打印作家中年齡不大于17的作家。

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

6.方法引用

我們在使用lambda時,如果方法體中只有一個方法的調用的話(包括構造方法),我們可以用方法引用進一步簡化代碼。

6.1推薦用法

我們在使用lambda時不需要考慮什么時候用方法引用,用哪種方法引用,方法引用的格式是什么。我們只需要在寫完lambda方法發現方法體只有一行代碼,并且是方法的調用時使用快捷鍵嘗試是否能夠轉傳成方法引用即可。
當我們方法引用使用的多了慢慢的也可以直接寫出方法引用。

6.2基本格式

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

6.3語法詳解(了解)

6.3.1引用類的靜態方法

其實就是引用類的靜態方法
格式

類名::方法名

使用前提

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

例如:

List<Author>authors = getAuthors();
Stream<Author> authorStream = authors.stream();
authorStream.map(author -> author.getAge()).map(new Function<Integer, String>() {@Overridepublic String apply(Integer age) {return String.valueOf(age);}});

優化后:

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

6.3.2引用對象的實例方法

格式

對象名::方法名

使用前提

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

List<Author>authors = getAuthors();
Stream<Author> authorStream = authors.stream();
StringBuilder sb = new StringBuilder();
authorStream.map(author -> author.getName()).forEach(new Consumer<String>() {@Overridepublic void accept(String name) {sb.append(name);}});

優化后:

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

6.3.4引用類的實例方法

格式

類名::方法名

使用前提

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

例如:

public class MethodDemo {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("hello world", new UseString() {@Overridepublic String use(String str, int start, int length) {return str.substring(start, length);}});}
}

優化后:

public class MethodDemo {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("hello world", String::substring);}
}

6.3.5構造器引用

如果方法體中的一行代碼是構造器的話就可以使用構造器引用。
格式

類名::new

使用前提

如果我們在重寫方法的時候,方法體中只有一行代碼,并且這行行代碼是調用了某個類的構造方法,并且我們把要重寫的抽象方去中的所有的參數都按照順序傳入了這個構造方法中,這個時候我們就可可以引用構造器
例如:

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

優化后:

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

7.高級用法

基本數據類型優化

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

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

所以為了讓我們能夠對這部分的時間消耗進行優化。Stream還還是供了很多專門針對基本數據類型的方法。

例如: mapToint,mapToLong,mapToDouble,flatMapT?lnt,flatMapToDouble等。

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

優化后:

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

并行流

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

parallel方法可以把串行流轉換成并行流。也可以通過parallelStream直接獲取并行流對象。

Stream<Integer> stream = Stream.of(1, 2, 3, 44, 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((x, y) -> x + y).get();
System.out.println(sum);

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/919893.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/919893.shtml
英文地址,請注明出處:http://en.pswp.cn/news/919893.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Overleaf 中文報錯和中文不顯示問題的解決方案

Overleaf是一個很方便的在線latex編輯工具。但在最初使用Overleaf的時候&#xff0c;是不是有很多小伙伴會遇到模板中中文報錯或者中文不顯示的問題呢&#xff1f; 本文將帶你一步步解決這個問題~ 中文報錯 在點擊重新編譯按鈕后&#xff0c;中文報錯問題一般會有如下圖紅框顯示…

前后端聯調場景以及可能會遇到的問題

一、異地和在一起辦公的方式 首先&#xff0c;在一起辦公&#xff08;同局域網&#xff09;的情況&#xff0c;最常用的應該是直接使用后端的局域網 IP 進行聯調&#xff0c;因為同一網絡內設備可以直接通信。步驟方面&#xff0c;需要后端提供 IP 和端口&#xff0c;前端配置…

【T113自制板卡】1 - 原理圖說明

文章目錄1、前言2、板卡資源總覽3、電源3.1、板卡供電3.2、電源方案4、OTG接口5、調試串口6、用戶LED7、FLASH8、按鍵9、BLE MESH10、Wi-Fi11、MIC12、喇叭接口13、MIPI接口1、前言 這幾天跟著小智學長的課程畫了一塊t113的板子。本文將描述該板卡的硬件說明。 2、板卡資源總…

WiFi有網絡但是電腦連不上網是怎么回事?該怎么解決?

有時候&#xff0c;咱們用電腦上網&#xff0c;打開WiFi一看&#xff0c;信號滿格&#xff0c;狀態欄顯示已連接&#xff0c;本來想著可以愉快地看個番、查個資料、玩個游戲了&#xff0c;結果一打開瀏覽器&#xff0c;直接完犢子了&#xff0c;網頁都打不開。這時候再看狀態&a…

【golang】制作linux環境+golang的Dockerfile | 如何下載golang鏡像源

一、關于如何下載docker images 這里需要大家自行科學上網如果沒有話&#xff0c;下面可以使用我自行打包的golang 的docker images 注意科學上網要開啟TUN模式二、golang鏡像源 1、阿里云公開鏡像 如果找不到golang包的小伙伴可以使用我的公開阿里鏡像docker pull registry.cn…

Day58 Java面向對象13 instanceof 和 類型轉換

Day58 Java面向對象13 instanceof 和 類型轉換 1.instanceof關鍵字 instanceof關鍵字的作用是判斷一個對象是否是某個類或其子類的實例,它返回一個布爾值true/false dog1 instanceof Dog; //返回true dog1 instanceof Animals; //返回true dog1 instanceof Object; //返回…

GEO優化案例:如何通過“知識圖譜+權威信號”提升品牌AI信任度

引言&#xff1a; “在AI日益成為用戶信息入口的今天&#xff0c;品牌信息能否被AI賦予‘權威’標簽&#xff0c;直接決定了其在搜索結果中的可見度和用戶采信度。移山科技正是這方面的專家。” 一、行業趨勢概覽&#xff1a;AI時代的品牌信任與GEO的價值 2025年&#xff0c…

讓數據可視化更簡單:Embedding Atlas使用指南

Embedding Atlas&#xff1a;交互式的嵌入可視化工具 在大數據時代&#xff0c;如何有效地理解和利用高維數據變得愈發重要。Embedding Atlas 是一款致力于提供大型嵌入&#xff08;embeddings&#xff09;交互式可視化的工具&#xff0c;允許用戶對嵌入數據進行可視化、交叉過…

復雜場景魯棒性突破!陌訊自適應融合算法在廠區越界檢測的實戰優化?

一、行業痛點&#xff1a;越界檢測的復雜場景挑戰 工業廠區周界安防中&#xff0c;越界檢測極易受環境干擾。據《2024工業智能安防白皮書》統計&#xff08;注1&#xff09;&#xff0c;強逆光、雨霧天氣導致傳統算法誤報率超35%&#xff0c;而密集設備遮擋造成的漏檢率高達28…

Huggingface入門實踐 Audio-NLP 語音-文字模型調用(一)

吳恩達LLM-Huggingface_嗶哩嗶哩_bilibili 目錄 0. huggingface 根據需求尋找開源模型 1. Whisper模型 語音識別任務 2. blenderbot 聊天機器人 3. 文本翻譯模型translator 4. BART 模型摘要器&#xff08;summarizer&#xff09; 5. sentence-transformers 句子相似度 …

Python-Pandas GroupBy 進階與透視表學習

??一、數據分組&#xff08;GroupBy&#xff09;????核心概念??&#xff1a;將數據按指定字段分組&#xff0c;對每組進行聚合、轉換或過濾操作。??1. 分組聚合&#xff08;Aggregation&#xff09;??將分組數據聚合成單個值&#xff08;如平均值、總和&#xff09…

MQTT 核心概念與協議演進全景解讀(二)

MQTT 在物聯網中的應用實例智能家居中的設備聯動在智能家居系統里&#xff0c;MQTT 協議扮演著至關重要的角色&#xff0c;是實現設備間高效通信與智能聯動的核心樞紐。以常見的智能家居場景為例&#xff0c;當清晨的陽光緩緩升起&#xff0c;光線傳感器檢測到光照強度的變化&a…

燧原科技招大模型訓練算法工程師

高級大模型訓練算法工程師&#xff08;崗位信息已經經過jobleap.cn授權&#xff0c;可在csdn發布&#xff09;燧原科技 上海職位描述負責大模型在AI芯片預訓練和微調等研發和客戶支持工作&#xff1b; 參與大模型訓練精度分析和性能調優&#xff1b;職位要求985/211大學計算機…

基于Java虛擬線程的高并發作業執行框架設計與性能優化實踐指南

基于Java虛擬線程的高并發作業執行框架設計與性能優化實踐指南 一、技術背景與應用場景 在分布式系統和微服務架構中&#xff0c;后端常需承載海量異步作業&#xff08;如批量數據處理、定時任務、異步消息消費等&#xff09;&#xff0c;對作業執行框架提出了高并發、高吞吐、…

了解 PostgreSQL 的 MVCC 可見性基本檢查規則

1. 引言 根據 Vadim Mikheev 的說法&#xff0c;PostgreSQL 的多版本并發控制&#xff08;MVCC&#xff09;是一種“在多用戶環境中提高數據庫性能的高級技術”。該技術要求系統中存在同一數據元組的多個“版本”&#xff0c;這些版本由不同時間段內獲取的快照進行管理。換句話…

普通烘箱 vs 鎧德科技防靜電烘箱:深度對比與選擇指南

在電子制造、化工、航空航天等精密工業領域&#xff0c;烘箱作為關鍵工藝設備&#xff0c;其性能直接關系到產品可靠性和生產安全。普通烘箱與防靜電烘箱的核心差異在于靜電防護能力&#xff0c;而鎧德科技作為防靜電烘箱領域的專業廠商&#xff0c;其產品通過技術創新重新定義…

達夢數據庫巡檢常用SQL(一)

達夢數據庫巡檢常用SQL(一) 數據庫基本信息 數據庫用戶信息 數據庫對象檢查 數據庫基本信息 檢查授權信息: SELECT /*+DMDB_CHECK_FLAG*/ LIC_VERSION AS "許可證版本" ,SERIES_NO AS "序列號" ,CHECK_CODE AS "校驗碼" …

TypeScript的接口 (Interfaces)講解

把接口&#xff08;Interface&#xff09;想成一份“說明書”或“合同書”。說明書 比如電飯煲的說明書告訴你&#xff1a; 必須有“煮飯”按鈕必須有“保溫”功能顏色可以是白、黑、紅 接口在 TypeScript 里干的就是同樣的事&#xff1a;它規定一個對象“長什么樣”。 interfa…

Python本源詩話(我DeepSeek)

物理折行新注釋&#xff0c;直抒胸臆吾志名。 筆記模板由python腳本于2025-08-23 13:14:28創建&#xff0c;本篇筆記適合喜歡python和詩的coder翻閱。 學習的細節是歡悅的歷程 博客的核心價值&#xff1a;在于輸出思考與經驗&#xff0c;而不僅僅是知識的簡單復述。 Python官網…

博士招生 | 美國圣地亞哥州立大學 Yifan Zhang 課題組博士招生,AI 安全領域頂尖平臺等你加入!

內容源自“圖靈學術博研社”gongzhonghao學校簡介圣地亞哥州立大學&#xff08;San Diego State University, SDSU&#xff09;是美國加州南部久負盛名的公立研究型大學。學校坐落于科技產業高度活躍的南加州地區&#xff0c;與本地軟件、電信、生物科技、國防及清潔能源等領域…