2018-11-11

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

1. 概述

1.1 為什么學(xué)饰及?

能夠看懂公司里的代碼

大數(shù)量下處理集合效率高

代碼可讀性高

消滅嵌套地獄

//查詢未成年作家的評分在70以上的書籍由于洋流影響所以作家和書籍可能出現(xiàn)重復(fù)础拨,需要進(jìn)行去重List bookList =newArrayList<>();Set uniqueBookValues =newHashSet<>();Set uniqueAuthorValues =newHashSet<>();for(Author author : authors) {if(uniqueAuthorValues.add(author)) {if(author.getAge() <18) {? ? ? ? ? ? List 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);

1.2 函數(shù)式編程思想

1.2.1 概念

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

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

代碼簡潔眶熬,開發(fā)快速

接近自然語言桑李,易于理解

易于"并發(fā)編程"

2. Lambda表達(dá)式

2.1 概述

Lambda是JDK8中一個(gè)語法糖。他可以對某些匿名內(nèi)部類的寫法進(jìn)行簡化恰响。它是函數(shù)式編程思想的一個(gè)重要體現(xiàn)趣钱。讓我們不用關(guān)注是什么對象。而是更關(guān)注我們對數(shù)據(jù)進(jìn)行了什么操作胚宦。

2.2 核心原則

可推導(dǎo)可省略

2. 3 基本格式

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

例一

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

newThread(newRunnable() {@Overridepublicvoidrun(){? ? ? ? System.out.println("你知道嗎我比你想象的更想在你身邊");? ? }}).start();

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

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

例二:

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

publicstaticintcalculateNum(IntBinaryOperator operator){inta =10;intb =20;returnoperator.applyAsInt(a, b);? ? }publicstaticvoidmain(String[] args){inti = calculateNum(newIntBinaryOperator() {@OverridepublicintapplyAsInt(intleft,intright){returnleft + right;? ? ? ? ? ? }? ? ? ? });? ? ? ? System.out.println(i);? ? }

Lambda寫法:

publicstaticvoidmain(String[] args){inti = calculateNum((intleft,intright)->{returnleft + right;? ? ? ? });? ? ? ? System.out.println(i);? ? }

例三:

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

publicstaticvoidprintNum(IntPredicate predicate){int[] arr = {1,2,3,4,5,6,7,8,9,10};for(inti : arr) {if(predicate.test(i)){? ? ? ? ? ? ? ? System.out.println(i);? ? ? ? ? ? }? ? ? ? }? ? }publicstaticvoidmain(String[] args){? ? ? ? printNum(newIntPredicate() {@Overridepublicbooleantest(intvalue){returnvalue%2==0;? ? ? ? ? ? }? ? ? ? });? ? }

Lambda寫法:

publicstaticvoidmain(String[] args){? ? ? ? printNum((intvalue)-> {returnvalue%2==0;? ? ? ? });? ? }publicstaticvoidprintNum(IntPredicate predicate){int[] arr = {1,2,3,4,5,6,7,8,9,10};for(inti : arr) {if(predicate.test(i)){? ? ? ? ? ? ? ? System.out.println(i);? ? ? ? ? ? }? ? ? ? }? ? }

例四:

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

publicstaticRtypeConver(Function<String,R> function){? ? ? ? String str ="1235";? ? ? ? R result = function.apply(str);returnresult;? ? }publicstaticvoidmain(String[] args){? ? ? ? Integer result = typeConver(newFunction() {@OverridepublicIntegerapply(String s){returnInteger.valueOf(s);? ? ? ? ? ? }? ? ? ? });? ? ? ? System.out.println(result);? ? }

Lambda寫法:

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

例五:

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

publicstaticvoidforeachArr(IntConsumer consumer){int[] arr = {1,2,3,4,5,6,7,8,9,10};for(inti : arr) {? ? ? ? ? ? consumer.accept(i);? ? ? ? }? ? }publicstaticvoidmain(String[] args){? ? ? ? foreachArr(newIntConsumer() {@Overridepublicvoidaccept(intvalue){? ? ? ? ? ? ? ? System.out.println(value);? ? ? ? ? ? }? ? ? ? });? ? }

Lambda寫法:

publicstaticvoidmain(String[] args){? ? ? ? foreachArr((intvalue)->{? ? ? ? ? ? System.out.println(value);? ? ? ? });? ? }

2.4 省略規(guī)則

參數(shù)類型可以省略

方法體只有一句代碼時(shí)大括號return和唯一一句代碼的分號可以省略

方法只有一個(gè)參數(shù)時(shí)小括號可以省略

以上這些規(guī)則都記不住也可以省略不記

3. Stream流

3.1 概述

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

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

org.projectlomboklombok1.18.16

@Data@NoArgsConstructor@AllArgsConstructor@EqualsAndHashCode//用于后期的去重使用publicclassAuthor{//idprivateLong id;//姓名privateString name;//年齡privateInteger age;//簡介privateString intro;//作品privateList books;}

@Data@AllArgsConstructor@NoArgsConstructor@EqualsAndHashCode//用于后期的去重使用publicclassBook{//idprivateLong id;//書名privateString name;//分類privateString category;//評分privateInteger score;//簡介privateString intro;}

privatestaticListgetAuthors(){//數(shù)據(jù)初始化Author author =newAuthor(1L,"蒙多",33,"一個(gè)從菜刀中明悟哲理的祖安人",null);? ? ? ? Author author2 =newAuthor(2L,"亞拉索",15,"狂風(fēng)也追逐不上他的思考速度",null);? ? ? ? Author author3 =newAuthor(3L,"易",14,"是這個(gè)世界在限制他的思維",null);? ? ? ? Author author4 =newAuthor(3L,"易",14,"是這個(gè)世界在限制他的思維",null);//書籍列表List books1 =newArrayList<>();? ? ? ? List books2 =newArrayList<>();? ? ? ? List books3 =newArrayList<>();? ? ? ? books1.add(newBook(1L,"刀的兩側(cè)是光明與黑暗","哲學(xué),愛情",88,"用一把刀劃分了愛恨"));? ? ? ? books1.add(newBook(2L,"一個(gè)人不能死在同一把刀下","個(gè)人成長,愛情",99,"講述如何從失敗中明悟真理"));? ? ? ? books2.add(newBook(3L,"那風(fēng)吹不到的地方","哲學(xué)",85,"帶你用思維去領(lǐng)略世界的盡頭"));? ? ? ? books2.add(newBook(3L,"那風(fēng)吹不到的地方","哲學(xué)",85,"帶你用思維去領(lǐng)略世界的盡頭"));? ? ? ? books2.add(newBook(4L,"吹或不吹","愛情,個(gè)人傳記",56,"一個(gè)哲學(xué)家的戀愛觀注定很難把他所在的時(shí)代理解"));? ? ? ? books3.add(newBook(5L,"你的劍就是我的劍","愛情",56,"無法想象一個(gè)武者能對他的伴侶這么的寬容"));? ? ? ? books3.add(newBook(6L,"風(fēng)與劍","個(gè)人傳記",100,"兩個(gè)哲學(xué)家靈魂和肉體的碰撞會激起怎么樣的火花呢洛心?"));? ? ? ? books3.add(newBook(6L,"風(fēng)與劍","個(gè)人傳記",100,"兩個(gè)哲學(xué)家靈魂和肉體的碰撞會激起怎么樣的火花呢?"));? ? ? ? author.setBooks(books1);? ? ? ? author2.setBooks(books2);? ? ? ? author3.setBooks(books3);? ? ? ? author4.setBooks(books3);? ? ? ? List authorList =newArrayList<>(Arrays.asList(author,author2,author3,author4));returnauthorList;? ? }

3.3 快速入門

3.3.1 需求

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

3.3.2 實(shí)現(xiàn)

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

3.4 常用操作

3.4.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 map =newHashMap<>();? ? ? ? map.put("蠟筆小新",19);? ? ? ? map.put("黑子",17);? ? ? ? map.put("日向翔陽",16);? ? ? ? Stream> stream = map.entrySet().stream();

3.4.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)行計(jì)算或轉(zhuǎn)換。

例如:

打印所有作家的姓名

? ? ? ? List<Author> authors = getAuthors();

? ? ? ? authors

? ? ? ? ? ? ? ? .stream()

? ? ? ? ? ? ? ? .map(author -> author.getName())

? ? ? ? ? ? ? ? .forEach(name->System.out.println(name));

//打印所有作家的姓名List authors = getAuthors();//? ? ? ? authors.stream()//? ? ? ? ? ? ? ? .map(author -> author.getName())//? ? ? ? ? ? ? ? .forEach(s -> System.out.println(s));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 authors = getAuthors();//對流中的元素按照年齡進(jìn)行降序排序垫挨,并且要求不能有重復(fù)的元素韩肝。authors.stream()? ? ? ? ? ? ? ? .distinct()? ? ? ? ? ? ? ? .sorted()? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getAge()));

List 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ù)的元素,然后打印其中年齡最大的兩個(gè)作家的姓名切威。

? ? ? ? List<Author> authors = getAuthors();

? ? ? ? authors.stream()

? ? ? ? ? ? ? ? .distinct()

? ? ? ? ? ? ? ? .sorted()

? ? ? ? ? ? ? ? .limit(2)

? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getName()));

skip

跳過流中的前n個(gè)元素育特,返回剩下的元素

例如:

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

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

flatMap

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

例一:

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

//打印所有書籍的名字迂苛。要求對重復(fù)的元素進(jìn)行去重三热。List 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 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.4.3 終結(jié)操作

forEach

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

例子:

輸出所有作家的名字

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

count

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

例子:

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

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

max&min

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

例子:

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

//分別獲取這些作家的所出書籍的最高分和最低分并打印。//Stream? -> Stream ->Stream? ->求值List authors = getAuthors();? ? ? ? Optional max = authors.stream()? ? ? ? ? ? ? ? .flatMap(author -> author.getBooks().stream())? ? ? ? ? ? ? ? .map(book -> book.getScore())? ? ? ? ? ? ? ? .max((score1, score2) -> score1 - score2);? ? ? ? Optional 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)換成一個(gè)集合棚壁。

例子:

獲取一個(gè)存放所有作者名字的List集合杯矩。

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

獲取一個(gè)所有書名的Set集合袖外。

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

獲取一個(gè)Map集合,map的key為作者名曼验,value為List<Book>

//獲取一個(gè)Map集合泌射,map的key為作者名,value為ListList authors = getAuthors();? ? ? ? Map> map = authors.stream()? ? ? ? ? ? ? ? .distinct()? ? ? ? ? ? ? ? .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));? ? ? ? System.out.println(map);

查找與匹配

anyMatch

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

例子:

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

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

allMatch

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

例子:

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

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

noneMatch

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

例子:

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

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

findAny

獲取流中的任意一個(gè)元素阴颖。該方法沒有辦法保證獲取的一定是流中的第一個(gè)元素。

例子:

獲取任意一個(gè)年齡大于18的作家丐膝,如果存在就輸出他的名字

//獲取任意一個(gè)年齡大于18的作家量愧,如果存在就輸出他的名字List authors = getAuthors();? ? ? ? Optional optionalAuthor = authors.stream()? ? ? ? ? ? ? ? .filter(author -> author.getAge()>18)? ? ? ? ? ? ? ? .findAny();? ? ? ? optionalAuthor.ifPresent(author -> System.out.println(author.getName()));

findFirst

獲取流中的第一個(gè)元素。

例子:

獲取一個(gè)年齡最小的作家帅矗,并輸出他的姓名偎肃。

//獲取一個(gè)年齡最小的作家,并輸出他的姓名浑此。List authors = getAuthors();? ? ? ? Optional first = authors.stream()? ? ? ? ? ? ? ? .sorted((o1, o2) -> o1.getAge() - o2.getAge())? ? ? ? ? ? ? ? .findFirst();? ? ? ? first.ifPresent(author -> System.out.println(author.getName()));

reduce歸并

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

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

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

T result = identity;for(T element :thisstream)result = accumulator.apply(result, element)returnresult;

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

例子:

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

//使用reduce求所有作者年齡的和List authors = getAuthors();? ? ? ? Integer sum = authors.stream()? ? ? ? ? ? ? ? .distinct()? ? ? ? ? ? ? ? .map(author -> author.getAge())? ? ? ? ? ? ? ? .reduce(0, (result, element) -> result + element);? ? ? ? System.out.println(sum);

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

//使用reduce求所有作者中年齡的最大值List 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 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一個(gè)參數(shù)的重載形式內(nèi)部的計(jì)算

booleanfoundAny =false;? ? T result =null;for(T element :thisstream) {if(!foundAny) {? ? ? ? ? ? foundAny =true;? ? ? ? ? ? result = element;? ? ? ? }elseresult = accumulator.apply(result, element);? ? }returnfoundAny ? Optional.of(result) : Optional.empty();

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

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

3.5 注意事項(xiàng)

惰性求值(如果沒有終結(jié)操作岸啡,沒有中間操作是不會得到執(zhí)行的)

流是一次性的(一旦一個(gè)流對象經(jīng)過一個(gè)終結(jié)操作后。這個(gè)流就不能再被使用)

不會影響原數(shù)據(jù)(我們在流中可以多數(shù)據(jù)做很多處理赫编。但是正常情況下是不會影響原來集合中的元素的凰狞。這往往也是我們期望的)

4. Optional

4.1 概述

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

例如:

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

尤其是對象中的屬性還是一個(gè)對象的情況下。這種判斷會更多达布。

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

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

并且在很多函數(shù)式編程相關(guān)的API中也都用到了Optional黍聂,如果不會使用Optional也會對函數(shù)式編程的學(xué)習(xí)造成影響躺苦。

4.2 使用

4.2.1 創(chuàng)建對象

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

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

? ? ? ? Author author = getAuthor();

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

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

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

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

Author author =newAuthor();? ? ? ? Optional authorOptional = Optional.of(author);

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

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

Optional.empty()

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

4.2.2 安全消費(fèi)值

我們獲取到一個(gè)Optional對象后肯定需要對其中的數(shù)據(jù)進(jìn)行使用捕捂。這時(shí)候我們可以使用其ifPresent方法對來消費(fèi)其中的值瑟枫。

這個(gè)方法會判斷其內(nèi)封裝的數(shù)據(jù)是否為空斗搞,不為空時(shí)才會執(zhí)行具體的消費(fèi)代碼。這樣使用起來就更加安全了慷妙。

例如,以下寫法就優(yōu)雅的避免了空指針異常僻焚。

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

? ? ? ? authorOptional.ifPresent(author -> System.out.println(author.getName()));

4.2.3 獲取值

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

4.2.4 安全獲取值

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

orElseGet

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

Optional authorOptional = Optional.ofNullable(getAuthor());? ? ? ? Author author1 = authorOptional.orElseGet(() ->newAuthor());

orElseThrow

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

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

4.2.5 過濾

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

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

? ? ? ? authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

4.2.6 判斷

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

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

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

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

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

privatestaticvoidtestMap(){? ? ? ? Optional authorOptional = getAuthorOptional();? ? ? ? Optional> optionalBooks = authorOptional.map(author -> author.getBooks());? ? ? ? optionalBooks.ifPresent(books -> System.out.println(books));? ? }

5. 函數(shù)式接口

5.1 概述

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

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

5.2 常見函數(shù)式接口

Consumer 消費(fèi)接口

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

Function 計(jì)算轉(zhuǎn)換接口

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

Predicate 判斷接口

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

Supplier 生產(chǎn)型接口

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

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

and

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

例如:

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

List authors = getAuthors();? ? ? ? Stream authorStream = authors.stream();? ? ? ? authorStream.filter(newPredicate() {@Overridepublicbooleantest(Author author){returnauthor.getAge()>17;? ? ? ? ? ? }? ? ? ? }.and(newPredicate() {@Overridepublicbooleantest(Author author){returnauthor.getName().length()>1;? ? ? ? ? ? }? ? ? ? })).forEach(author -> System.out.println(author));

or

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

例如:

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

//打印作家中年齡大于17或者姓名的長度小于2的作家前弯。List authors = getAuthors();? ? ? ? authors.stream()? ? ? ? ? ? ? ? .filter(newPredicate() {@Overridepublicbooleantest(Author author){returnauthor.getAge()>17;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }.or(newPredicate() {@Overridepublicbooleantest(Author author){returnauthor.getName().length()<2;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? })).forEach(author -> System.out.println(author.getName()));

negate

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

例如:

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

//打印作家中年齡不大于17的作家恕出。List authors = getAuthors();? ? ? ? authors.stream()? ? ? ? ? ? ? ? .filter(newPredicate() {@Overridepublicbooleantest(Author author){returnauthor.getAge()>17;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }.negate()).forEach(author -> System.out.println(author.getAge()));

6. 方法引用

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

6.1 推薦用法

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

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

6.2 基本格式

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

6.3 語法詳解(了解)

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

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

格式

類名::方法名

使用前提

如果我們在重寫方法的時(shí)候苗傅,方法體中只有一行代碼,并且這行代碼是調(diào)用了某個(gè)類的靜態(tài)方法班巩,并且我們把要重寫的抽象方法中所有的參數(shù)都按照順序傳入了這個(gè)靜態(tài)方法中渣慕,這個(gè)時(shí)候我們就可以引用類的靜態(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);

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

格式

對象名::方法名

使用前提

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

例如:

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

優(yōu)化后:

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

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

格式

類名::方法名

使用前提

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

例如:

interfaceUseString{Stringuse(String str,intstart,intlength);? ? }publicstaticStringsubAuthorName(String str, UseString useString){intstart =0;intlength =1;returnuseString.use(str,start,length);? ? }publicstaticvoidmain(String[] args){? ? ? ? subAuthorName("三更草堂",newUseString() {@OverridepublicStringuse(String str,intstart,intlength){returnstr.substring(start,length);? ? ? ? ? ? }? ? ? ? });}

優(yōu)化后如下:

publicstaticvoidmain(String[] args){? ? ? ? subAuthorName("三更草堂", String::substring);? ? }

6.3.5 構(gòu)造器引用

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

格式

類名::new

使用前提

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

例如:

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

優(yōu)化后:

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

7. 高級用法

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

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

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

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

例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等毕谴。

privatestaticvoidtest27(){? ? ? ? List 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í),我們可以使用并行流去提高操作的效率距芬。其實(shí)并行流就是把任務(wù)分配給多個(gè)線程去完全涝开。如果我們自己去用代碼實(shí)現(xiàn)的話其實(shí)會非常的復(fù)雜,并且要求你對并發(fā)編程有足夠的理解和認(rèn)識框仔。而如果我們使用Stream的話舀武,我們只需要修改一個(gè)方法的調(diào)用就可以使用并行流來幫我們實(shí)現(xiàn),從而提高效率离斩。

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

privatestaticvoidtest28(){? ? ? ? Stream stream = Stream.of(1,2,3,4,5,6,7,8,9,10);? ? ? ? Integer sum = stream.parallel()? ? ? ? ? ? ? ? .peek(newConsumer() {@Overridepublicvoidaccept(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);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跛梗,一起剝皮案震驚了整個(gè)濱河市寻馏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌核偿,老刑警劉巖诚欠,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漾岳,居然都是意外死亡聂薪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門蝗羊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藏澳,“玉大人,你說我怎么就攤上這事耀找∠栌疲” “怎么了业崖?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蓄愁。 經(jīng)常有香客問我双炕,道長,這世上最難降的妖魔是什么撮抓? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任妇斤,我火速辦了婚禮,結(jié)果婚禮上丹拯,老公的妹妹穿的比我還像新娘站超。我一直安慰自己,他們只是感情好乖酬,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布死相。 她就那樣靜靜地躺著,像睡著了一般咬像。 火紅的嫁衣襯著肌膚如雪算撮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天县昂,我揣著相機(jī)與錄音肮柜,去河邊找鬼。 笑死倒彰,一個(gè)胖子當(dāng)著我的面吹牛素挽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狸驳,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缩赛!你這毒婦竟也來了耙箍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤酥馍,失蹤者是張志新(化名)和其女友劉穎辩昆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旨袒,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汁针,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砚尽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片施无。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖必孤,靈堂內(nèi)的尸體忽然破棺而出猾骡,到底是詐尸還是另有隱情瑞躺,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布兴想,位于F島的核電站幢哨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嫂便。R本人自食惡果不足惜捞镰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毙替。 院中可真熱鬧岸售,春花似錦、人聲如沸蔚龙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽木羹。三九已至甲雅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坑填,已是汗流浹背抛人。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脐瑰,地道東北人妖枚。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像苍在,于是被迫代替她去往敵國和親绝页。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 如何新建一個(gè)線程寂恬? 線程狀態(tài)是怎樣轉(zhuǎn)換的续誉? 關(guān)于線程狀態(tài)的操作是怎樣的? 1. 新建線程 一個(gè)java程序從mai...
    凝云紫影閱讀 155評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理初肉,服務(wù)發(fā)現(xiàn)酷鸦,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • Java8新特性[https://www.altitude.xin/blog/home/#/chapter/0c2...
    Java編程日記閱讀 329評論 0 0
  • JavaLambda表達(dá)式的一個(gè)重要用法是簡化某些匿名內(nèi)部類(Anonymous Classes)的寫法牙咏。實(shí)際上L...
    idaretobee閱讀 896評論 0 0
  • ## 代碼層級梳理 #### 代碼層級 ![UTOOLS1588253419016.png](https://jo...
    Johnny韓源閱讀 1,279評論 0 1