Java8中怎么實現(xiàn)Stream流式操作,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
創(chuàng)新互聯(lián)公司是一家朝氣蓬勃的網(wǎng)站建設(shè)公司。公司專注于為企業(yè)提供信息化建設(shè)解決方案。從事網(wǎng)站開發(fā),網(wǎng)站制作,網(wǎng)站設(shè)計,網(wǎng)站模板,微信公眾號開發(fā),軟件開發(fā),小程序開發(fā),十年建站對社區(qū)文化墻等多個領(lǐng)域,擁有多年建站經(jīng)驗。
流是一種以聲明性的方式來處理數(shù)據(jù)的API
什么是聲明性的方式?
就是只聲明,不實現(xiàn),類似抽象方法(多態(tài)性)
下面我們舉個栗子,來看下什么是流式操作,然后針對這個栗子,引出后面的相關(guān)概念
需求:篩選年齡大于1的貓(貓的1年≈人的5年),并按年齡遞增排序,最后提取名字單獨存放到列表中
public class BasicDemo { public static void main(String[] args) { // 以下貓的名字均為真名,非虛構(gòu) List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu")); // === 舊代碼 Java8之前 === List<Cat> listTemp = new ArrayList<>(); // 1. 篩選 for(Cat cat: list){ if(cat.getAge()>1){ listTemp.add(cat); } } // 2. 排序 listTemp.sort(new Comparator<Cat>() { @Override public int compare(Cat o1, Cat o2) { // 遞增排序 return Integer.compare(o1.getAge(), o2.getAge()); } }); // 3. 提取名字 List<String> listName = new ArrayList<>(); for(Cat cat: listTemp){ listName.add(cat.getName()); } System.out.println(listName); // === 新代碼 Java8之后 === List<String> listNameNew = list.stream() // 函數(shù)式接口 Predicate的 boolean test(T t)抽象方法 .filter(cat -> cat.getAge() > 1) // lambda表達式的方法引用 .sorted(Comparator.comparingInt(Cat::getAge)) // 函數(shù)式接口 Funtion的 R apply(T t)抽象方法 .map(cat-> cat.getName()) // 收集數(shù)據(jù),把流轉(zhuǎn)為集合List .collect(Collectors.toList()); System.out.println(listNameNew); } } class Cat{ int age; String name; public Cat(int age, String name) { this.age = age; this.name = name; } // 省略getter/setter }
可以看到,用了流式操作,代碼簡潔了很多(秒哇)
Q:有的官人可能會想,這跟前面lambda表達式的組合操作有點像啊。
A:你說對了,確實只是像,區(qū)別還是很大的。因為lambda表達式的組合操作其實還是屬于直接針對集合的操作;
文末會講到直接操作集合和流式操作的區(qū)別,這里先跳過
下面我們基于這個栗子,來分別介紹涉及到的知識點
我們先忽略舊版的集合操作(后面介紹流和集合的區(qū)別時再說),先來介紹流的操作(畢竟流才是今天的主角嘛)
流的操作分三步走:創(chuàng)建流、中間操作、終端操作
流程如下圖:
這里我們要關(guān)注一個很重要的點:
在終端操作開始之前,中間操作不會執(zhí)行任何處理,它只是聲明執(zhí)行什么操作;
你可以想象上面這個流程是一個流水線:我們這里做個簡化處理
目的:先告訴你,我們要加工瓶裝的水(先創(chuàng)建流,告訴你要處理哪些數(shù)據(jù))
再針對這些瓶子和水,來搭建一個流水線:固定瓶子的夾具、裝水的水管、擰蓋子的爪子、裝箱的打包器(中間操作,聲明要執(zhí)行的操作)
最后按下啟動按鈕,流水線開始工作(終端操作,開始根據(jù)中間操作來處理數(shù)據(jù))
因為每一個中間操作都是返回一個流(Stream),這樣他們就可以一直組合下去(我好像吃到了什么東西?),但是他們的組合順序是不固定的,流會根據(jù)系統(tǒng)性能去選擇合適的組合順序
我們可以打印一些東西來看下:
List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu")); List<String> listNameNew = list.stream() .filter(cat -> { System.out.println("filter: " + cat); return cat.getAge() > 1; }) .map(cat-> { System.out.println("map:" + cat); return cat.getName(); }) .collect(Collectors.toList());
輸出如下:
filter: Cat{age=1} filter: Cat{age=3} map:Cat{age=3} filter: Cat{age=2} map:Cat{age=2}
可以看到,中間操作的filter和map組合到一起交叉執(zhí)行了,盡管他們是兩個獨立的操作(這個技術(shù)叫作循環(huán)合并)
這個合并主要是由流式操作根據(jù)系統(tǒng)的性能來自行決定的
既然講到了循環(huán)合并,那下面捎帶說下短路技巧
短路這個詞大家應(yīng)該比較熟悉(比如腦子短路什么的),指的是本來A->B->C是都要執(zhí)行的,但是在B的地方短路了,所以就變成了A->C了
這里的短路指的是中間操作,由于某些原因(比如下面的limit),導(dǎo)致只執(zhí)行了部分,沒有全部去執(zhí)行
我們來修改下上面的例子(加了一個中間操作limit):
List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu")); List<String> listNameNew = list.stream() .filter(cat -> { System.out.println("filter: " + cat); return cat.getAge() > 1; }) .map(cat-> { System.out.println("map:" + cat); return cat.getName(); }) // 只加了這一行 .limit(1) .collect(Collectors.toList());
輸出如下:
filter: Cat{age=1} filter: Cat{age=3} map:Cat{age=3}
可以看到,因為limit(1)只需要一個元素,所以filter過濾時,只要找到一個滿足條件的,就會停止過濾操作(后面的元素就放棄了),這個技巧叫做短路技巧
這個就很大程度上體現(xiàn)了中間操作的組合順序帶來的優(yōu)點:需要多少,處理多少,即按需處理
特點有三個:
聲明性:簡潔,易讀,代碼行數(shù)大大減少(每天有代碼行數(shù)要求的公司除外)
可復(fù)合:更靈活,各種組合都可以(只要你想,只要流有)
可并行:性能更好(不用我們?nèi)懚嗑€程,多好)
現(xiàn)在我們再來回顧下開頭例子中的集合操作:篩選->排序->提取
List<Cat> listTemp = new ArrayList<>(); // 1. 篩選 for(Cat cat: list){ if(cat.getAge()>1){ listTemp.add(cat); } } // 2. 排序 listTemp.sort(new Comparator<Cat>() { @Override public int compare(Cat o1, Cat o2) { // 遞增排序 return Integer.compare(o1.getAge(), o2.getAge()); /** * Q:為啥不用減法 return o1.getAge() - o2.getAge()? * A:因為減法會有數(shù)據(jù)溢出的風(fēng)險 * 如果o1.getAge()為20億,o2.getAge()為-2億,那么結(jié)果就會超過int的極限21億多 **/ } }); // 3. 提取名字 List<String> listName = new ArrayList<>(); for(Cat cat: listTemp){ listName.add(cat.getName()); } System.out.println(listName);
可以看到跟流式操作不一樣的有兩點:
集合操作中有一個listTemp臨時變量(流式操作沒),
集合操作一直都在處理數(shù)據(jù)(而流式操作是直到最后一步的終端操作才會去處理數(shù)據(jù)),依次篩選->排序->提取名字,是順序執(zhí)行的
下面我們用表格來列出區(qū)別,應(yīng)該會直觀點
流式操作 | 集合操作 | |
---|---|---|
功能 | 處理數(shù)據(jù)為主 | 存儲數(shù)據(jù)為主 |
迭代方式 | 內(nèi)部迭代(只迭代一次),只需聲明,不需要實現(xiàn),流內(nèi)部自己有實現(xiàn)) | 外部迭代(可一直迭代)需要自己foreach |
處理數(shù)據(jù) | 直到終端操作,才會開始真正處理數(shù)據(jù)(按需處理) | 一直都在處理數(shù)據(jù)(全部處理) |
用生活中的例子來對比的話,可以用電影來比喻
流就好比在線觀看,集合就好本地觀看(下載到本地)
流是什么:
源:數(shù)據(jù)的來源,比如集合,文件等(本節(jié)只介紹了集合的流式操作,因為用的比較多;后面有空再介紹其他的)
數(shù)據(jù)處理操作:就是流的中間操作,比如filter, map
元素序列:通過流的終端操作,返回的結(jié)果集
流是一種以聲明性的方式來處理數(shù)據(jù)的API
流是從支持數(shù)據(jù)處理操作的源生成的元素序列
流的操作流程:
創(chuàng)建流 -> 中間操作 -> 終端操作
中間操作只是聲明,不真實處理數(shù)據(jù),直到終端操作開始才會執(zhí)行
循環(huán)合并:中間操作會自由組合(流根據(jù)系統(tǒng)自己來決定組合的順序)
短路技巧:如果中間操作處理的數(shù)據(jù)已經(jīng)達到需求,則會立即停止處理數(shù)據(jù)(比如limit(1),則當(dāng)處理完1個就會停止處理)
流式操作和集合操作的區(qū)別:
流按需處理,集合全處理
流主攻數(shù)據(jù)處理,集合主攻數(shù)據(jù)存儲
流簡潔,集合不
流內(nèi)部迭代(只迭代一次,完后流會消失),集合外部迭代(可一直迭代)
看完上述內(nèi)容,你們掌握Java8中怎么實現(xiàn)Stream流式操作的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
新聞標(biāo)題:Java8中怎么實現(xiàn)Stream流式操作
文章位置:http://muchs.cn/article6/ijoiig.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)站收錄、網(wǎng)頁設(shè)計公司、面包屑導(dǎo)航、定制開發(fā)、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)