函數(shù)式編程-Stream流
能夠看懂公司里的代碼
大數(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);
面向?qū)ο笏枷胄枰P(guān)注用什么對象完成什么事情寞缝。而函數(shù)式編程思想就類似于我們數(shù)學(xué)中的函數(shù)倒信。它主要關(guān)注的是對數(shù)據(jù)進(jìn)行了什么操作。
代碼簡潔眶熬,開發(fā)快速
接近自然語言桑李,易于理解
易于"并發(fā)編程"
Lambda是JDK8中一個(gè)語法糖。他可以對某些匿名內(nèi)部類的寫法進(jìn)行簡化恰响。它是函數(shù)式編程思想的一個(gè)重要體現(xiàn)趣钱。讓我們不用關(guān)注是什么對象。而是更關(guān)注我們對數(shù)據(jù)進(jìn)行了什么操作胚宦。
可推導(dǎo)可省略
(參數(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);? ? ? ? });? ? }
參數(shù)類型可以省略
方法體只有一句代碼時(shí)大括號return和唯一一句代碼的分號可以省略
方法只有一個(gè)參數(shù)時(shí)小括號可以省略
以上這些規(guī)則都記不住也可以省略不記
Java8的Stream使用的是函數(shù)式編程模式,如同它的名字一樣尺碰,它可以被用來對集合或數(shù)組進(jìn)行鏈狀流式的操作挣棕。可以更方便的讓我們對集合或數(shù)組操作亲桥。
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;? ? }
我們可以調(diào)用getAuthors方法獲取到作家的集合√馀瘢現(xiàn)在需要打印所有年齡小于18的作家的名字词身,并且要注意去重。
//打印所有年齡小于18的作家的名字番枚,并且要注意去重List authors = getAuthors();? ? ? ? authors.? ? ? ? ? ? ? ? stream()//把集合轉(zhuǎn)換成流.distinct()//先去除重復(fù)的作家.filter(author -> author.getAge()<18)//篩選年齡小于18的.forEach(author -> System.out.println(author.getName()));//遍歷打印名字
單列集合:?集合對象.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();
可以對流中的元素進(jìn)行條件過濾偿枕,符合過濾條件的才能繼續(xù)留在流中璧瞬。
例如:
打印所有姓名長度大于1的作家的姓名
? ? ? ? List<Author> authors = getAuthors();
? ? ? ? authors.stream()
? ? ? ? ? ? ? ? .filter(author -> author.getName().length()>1)
? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getName()));
可以把對流中的元素進(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));
可以去除流中的重復(fù)元素渐夸。
例如:
打印所有作家的姓名,并且要求其中不能有重復(fù)元素渔欢。
? ? ? ? List<Author> authors = getAuthors();
? ? ? ? authors.stream()
? ? ? ? ? ? ? ? .distinct()
? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getName()));
注意:distinct方法是依賴Object的equals方法來判斷是否是相同對象的墓塌。所以需要注意重寫equals方法。
可以對流中的元素進(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。
可以設(shè)置流的最大長度哲泊,超出的部分將被拋棄剩蟀。
例如:
對流中的元素按照年齡進(jìn)行降序排序,并且要求不能有重復(fù)的元素,然后打印其中年齡最大的兩個(gè)作家的姓名切威。
? ? ? ? List<Author> authors = getAuthors();
? ? ? ? authors.stream()
? ? ? ? ? ? ? ? .distinct()
? ? ? ? ? ? ? ? .sorted()
? ? ? ? ? ? ? ? .limit(2)
? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getName()));
跳過流中的前n個(gè)元素育特,返回剩下的元素
例如:
打印除了年齡最大的作家外的其他作家,要求不能有重復(fù)元素先朦,并且按照年齡降序排序缰冤。
//打印除了年齡最大的作家外的其他作家,要求不能有重復(fù)元素喳魏,并且按照年齡降序排序棉浸。List authors = getAuthors();? ? ? ? authors.stream()? ? ? ? ? ? ? ? .distinct()? ? ? ? ? ? ? ? .sorted()? ? ? ? ? ? ? ? .skip(1)? ? ? ? ? ? ? ? .forEach(author -> System.out.println(author.getName()));
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));
對流中的元素進(jìn)行遍歷操作念搬,我們通過傳入的參數(shù)去指定對遍歷到的元素進(jìn)行什么具體操作抑堡。
例子:
輸出所有作家的名字
//輸出所有作家的名字List authors = getAuthors();? ? ? ? authors.stream()? ? ? ? ? ? ? ? .map(author -> author.getName())? ? ? ? ? ? ? ? .distinct()? ? ? ? ? ? ? ? .forEach(name-> System.out.println(name));
可以用來獲取當(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);
可以用來或者流中的最值有缆。
例子:
分別獲取這些作家的所出書籍的最高分和最低分并打印象踊。
//分別獲取這些作家的所出書籍的最高分和最低分并打印。//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());
把當(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);
可以用來判斷是否有任意符合匹配條件的元素蚣驼,結(jié)果為boolean類型魄幕。
例子:
判斷是否有年齡在29以上的作家
//判斷是否有年齡在29以上的作家List authors = getAuthors();booleanflag = authors.stream()? ? ? ? ? ? ? ? .anyMatch(author -> author.getAge() >29);? ? ? ? System.out.println(flag);
可以用來判斷是否都符合匹配條件,結(jié)果為boolean類型颖杏。如果都符合結(jié)果為true纯陨,否則結(jié)果為false。
例子:
判斷是否所有的作家都是成年人
//判斷是否所有的作家都是成年人List authors = getAuthors();booleanflag = authors.stream()? ? ? ? ? ? ? ? .allMatch(author -> author.getAge() >=18);? ? ? ? System.out.println(flag);
可以判斷流中的元素是否都不符合匹配條件留储。如果都不符合結(jié)果為true翼抠,否則結(jié)果為false
例子:
判斷作家是否都沒有超過100歲的。
//判斷作家是否都沒有超過100歲的获讳。List authors = getAuthors();booleanb = authors.stream()? ? ? ? ? ? ? ? .noneMatch(author -> author.getAge() >100);? ? ? ? System.out.println(b);
獲取流中的任意一個(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()));
獲取流中的第一個(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()));
對流中的數(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));
惰性求值(如果沒有終結(jié)操作岸啡,沒有中間操作是不會得到執(zhí)行的)
流是一次性的(一旦一個(gè)流對象經(jīng)過一個(gè)終結(jié)操作后。這個(gè)流就不能再被使用)
不會影響原數(shù)據(jù)(我們在流中可以多數(shù)據(jù)做很多處理赫编。但是正常情況下是不會影響原來集合中的元素的凰狞。這往往也是我們期望的)
我們在編寫代碼的時(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í)造成影響躺苦。
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
我們獲取到一個(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()));
如果我們想獲取值自己進(jìn)行處理可以使用get方法獲取,但是不推薦膝擂。因?yàn)楫?dāng)Optional內(nèi)部的數(shù)據(jù)為空的時(shí)候會出現(xiàn)異常虑啤。
如果我們期望安全的獲取值。我們不推薦使用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();? ? ? ? }
我們可以使用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()));
我們可以使用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());? ? ? ? }
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));? ? }
只有一個(gè)抽象方法的接口我們稱之為函數(shù)接口。
JDK的函數(shù)式接口都加上了**@FunctionalInterface** 注解進(jìn)行標(biāo)識穿挨。但是無論是否加上該注解只要接口中只有一個(gè)抽象方法月弛,都是函數(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)建好的對象返回
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()));
我們在使用lambda時(shí)询枚,如果方法體中只有一個(gè)方法的調(diào)用的話(包括構(gòu)造方法),我們可以用方法引用進(jìn)一步簡化代碼。
我們在使用lambda時(shí)不需要考慮什么時(shí)候用方法引用浙巫,用哪種方法引用哩盲,方法引用的格式是什么。我們只需要在寫完lambda方法發(fā)現(xiàn)方法體只有一行代碼狈醉,并且是方法的調(diào)用時(shí)使用快捷鍵嘗試是否能夠轉(zhuǎn)換成方法引用即可。
當(dāng)我們方法引用使用的多了慢慢的也可以直接寫出方法引用惠险。
類名或者對象名::方法名
其實(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);
對象名::方法名
如果我們在重寫方法的時(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);
類名::方法名
如果我們在重寫方法的時(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);? ? }
如果方法體中的一行代碼是構(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));
我們之前用到的很多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);