Stream的特性、用法、stream().map().collect()用法


1.舉例說明

有一個集合:

List<User> users = getList(); 

現在想獲取User的角色;在后續的邏輯處理中使用;

第一種方法,用for循環:

//定義一個集合存放用戶角色
 List<String> role = new ArrayList<String>(); for(int i=0;i<users.size();i++){   role .add(users.get(i).getRole()); }

這種方法要寫好幾行代碼,有沒有簡單點的,有,java8 API能一行搞定:

第二種方法:用stream代替for或者foreach循環

List<String> role = users.stream().map(User::getRole).collect(Collectors.toList())

解釋下一這行代碼:

users:一個實體類的集合,類型為List<User>
User:實體類
getRole:實體類中的get方法,為獲取User的角色

2.Stream的特性

stream()優點:無存儲。對大數據量的集合的循環處理,stream擁有極大的優勢,完全可以用stream去代替for循環。

stream()介紹:是java對集合操作的優化,相較於迭代器,使用Stream的速度非常快,並且它支持並行方式處理集合中的數據,默認情況能充分利用cpu的資源。同時支持函數式編程,代碼非常簡潔。

        Stream是一種用來計算數據的流,它本身並沒有存儲數據。你可以認為它是對數據源的一個映射或者視圖。

        Stream的工作流程是:獲取數據源->進行一次或多次邏輯轉換操作->進行歸約操作形成新的流(最后可以將流轉換成集合)。

        stream不是一種數據結構,它只是某種數據源的一個視圖,數據源可以是一個數組,Java容器或I/O channel等。
        為函數式編程而生。對stream的任何修改都不會修改背后的數據源,比如對stream執行過濾操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream。
        惰式執行:stream上的操作並不會立即執行,只有等到用戶真正需要結果的時候才會執行。
        可消費性:stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。

 

中間操作惰性執行:一個流后面可以跟隨0到多個中間操作,主要目的是打開流,並沒有真正的去計算,而是做出某種程度的數據映射 /過濾,然后返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷,並沒有消耗資源。
還有多個中間操作的話,這里的時間復雜度並不是n個for循環,轉換操作都是 lazy 的,多個轉換操作只會在 Terminal 操作的時候融合起來,一次循環完成。可以這樣簡單的理解,Stream 里有個操作函數的集合,每次轉換操作就是把轉換函數放入這個集合中,在Terminal操作的時候循環 Stream 對應的集合,然后對每個元素執行所有的函數。

流的末端操作只能有一次: 當這個操作執行后,流就被使用“光”了,無法再被操作。所以這必定是流的最后一個操作。之后如果想要操作就必須新打開流。

 

關於流被關閉不能再操作的異常:

這里曾經遇到過一個錯誤:stream has already been operated upon or closed

意思是流已經被關閉了,這是因為當我們使用末端操作之后,流就被關閉了,無法再次被調用,如果我們想重復調用,只能重新打開一個新的流。

3.stream().map(A::B).collect.(Collectors.toList()).contain("**")用法

 //獲取所有用戶列表信息

List<Role> roleList = UserUtils.getUser().getRoleList();
 
        
Boolean flag = roleList.stream().map(Role::getEnname).collect(Collectors.toList()).contains("James");
解釋一下,上一句的含義:
//1.將用戶信息列表轉化為流的形式(用stream 代替了for和foreach循環)
//2.以map的數據格式獲取所有用戶的Enname
//3.然后把所有的Enname放到一個collect集合中,然后轉為List類型
//4.最后判斷該List中是否包含字符串"James"

4.性能比較

如果數據在1萬以內的話,for循環效率高於foreach和stream;如果數據量在10萬的時候,stream效率最高,其次是foreach,最后是for。

另外需要注意的是如果數據達到100萬的話,parallelStream異步並行處理效率最高,高於foreach和for。

5.stream用法之排序

sorted提供了2個接口:

1、 sorted() 默認使用自然序排序, 其中的元素必須實現 Comparable 接口 。
2、 sorted(Comparator<? super T> comparator) :我們可以使用lambada 來創建一個 Comparator 實例。可以按照升序或着降序來排序元素。 
注意sorted是一個有狀態的中間操作,即,只有全部執行完所有的數據才能知道結果。

比如:將一些字符串在地址中按出現的順序排列:

public static void main(String[] args){
String address = "門前大橋下游過一群鴨";
List<String> list= new ArrayList<>();
list.add("一群鴨");
list.add("大橋下");
list.add("門前");
list.add("游過");
list.add("我家");
list.stream().sorted(
Comparator.comparing(a->address.indexOf(a))).forEach(System.out :: println);
}

可以不使用比較器:

 //按地址由大到小排序
 list.stream().sorted( (a,b)->address.IndexOf(a)-address.IndexOf(b)).forEach(System.out :: println);

 運行結果:

我家 門前 大橋下 游過 一群鴨 Process finished with exit code 0

注:1.“我家” 這個字段在地址定義中並沒有出現,但是在list中添加了,所以序號是-1(默認序號-1),被排在最前面,其他字段按address中的地址大小,從大到小排序

  2.Comparator.comparing();這個是比較器提供的一個方法,它返回的也是一個比較器,源碼如下:

public static <T, U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }

6.stream用法之篩選

上一步中,我們把一些字符串,按照在地址中出現的順序排序。

接下來我們可能想要進行篩選,把不在地址中,但是indexof為“-1”,排在最前面的數據篩選掉:

filter可以對集合進行篩選,它的參數可以是一個lambda表達式,流中的數據將會通過該lambda表達式返回新的流。

這里Stream有一個特性很重要,它像一個管道,可以將多個操作連接起來,並只執行一次for循環,這樣大大提高了效率,即使第二次的流操作需要第一次流操作的結果,時間復雜度也只有一個for循環:

於是我可以在前面加個filter(),這樣把“-1”過濾掉:  即把"我家"過濾掉

  String address = "門前大橋下游過一群鴨";
List<String> list= new ArrayList<>();
list.add("一群鴨");
list.add("大橋下");
list.add("門前");
list.add("游過");
list.add("我家");
list.stream().filter(a->address.indexOf(a)!=-1).sorted( Comparator.comparing(a->address.indexOf(a)) ).forEach(System.out :: println);

運行結果:

門前
大橋下
游過
一群鴨

Process finished with exit code 0

注:foreach是一個終端操作,參數也是一個函數,它會迭代中間操作完成后的每一個數據,這里它將每個不為空的元素打印出來。

其它的過濾操作還包括:

limit(long maxSize):獲得指定數量的流。

distinct():通過hashCode和equals去除重復元素。

7.stream用法之映射

映射操作,就像一個管道,可以將流中的元素通過一個函數進行映射,返回一個新的元素。

這樣遍歷映射,最終返回一個新的容器,注意:這里返回的新容器數據類型可以不與原容器類型相同:

舉個例子:我們將address中每個元素的位置找出,並返回一個int類型的存儲位置信息的數組:

public static void main(String[] args){ String address = "門前大橋下游過一群鴨"; List<String> list= new ArrayList<>(); list.add("一群鴨"); list.add("大橋下"); list.add("門前"); list.add("游過"); list.add("我家");  List<Integer> aIntegers =list.stream() .map(str->mapPrintIndex(address, str)).collect(Collectors.toList()); System.out.println(aIntegers); } private static int mapPrintIndex(String address,String str) { return address.indexOf(str); }

運行結果:

[7, 2, 0, 5, -1]

Process finished with exit code 0

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM