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只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。
流的末端操作只能有一次: 當這個操作執行后,流就被使用“光”了,無法再被操作。所以這必定是流的最后一個操作。之后如果想要操作就必須新打開流。
關於流被關閉不能再操作的異常:
這里曾經遇到過一個錯誤: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
實例。可以按照升序或着降序來排序元素。
比如:將一些字符串在地址中按出現的順序排列:
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