Java8 新特性之集合操做Streamjava
Stream簡介
- Java 8引入了全新的Stream API。這裏的Stream和I/O流不一樣,它更像具備Iterable的集合類,但行爲和集合類又有所不一樣。
- stream是對集合對象功能的加強,它專一於對集合對象進行各類很是便利、高效的聚合操做,或者大批量數據操做。
爲何要使用Stream
- 函數式編程帶來的好處尤其明顯。這種代碼更多地表達了業務邏輯的意圖,而不是它的實現機制。易讀的代碼也易於維護、更可靠、更不容易出錯。
- 高端
使用實例:編程
測試數據:
public class Data { private static List<PersonModel> list = null; static { PersonModel wu = new PersonModel("wu qi", 18, "男"); PersonModel zhang = new PersonModel("zhang san", 19, "男"); PersonModel wang = new PersonModel("wang si", 20, "女"); PersonModel zhao = new PersonModel("zhao wu", 20, "男"); PersonModel chen = new PersonModel("chen liu", 21, "男"); list = Arrays.asList(wu, zhang, wang, zhao, chen); } public static List<PersonModel> getData() { return list; } }
Filter
- 遍歷數據並檢查其中的元素時使用。
- filter接受一個函數做爲參數,該函數用Lambda表達式表示。
保留年齡爲 20 的 person 元素 list = list.stream() .filter(person -> person.getAge() == 20) .collect(toList()); 打印輸出 [Person{name='jack', age=20}]
/** * 過濾全部的男性 */ public static void fiterSex(){ List<PersonModel> data = Data.getData(); //old List<PersonModel> temp=new ArrayList<>(); for (PersonModel person:data) { if ("男".equals(person.getSex())){ temp.add(person); } } System.out.println(temp); //new List<PersonModel> collect = data .stream() .filter(person -> "男".equals(person.getSex())) .collect(toList()); System.out.println(collect); } /** * 過濾全部的男性 而且小於20歲 */ public static void fiterSexAndAge(){ List<PersonModel> data = Data.getData(); //old List<PersonModel> temp=new ArrayList<>(); for (PersonModel person:data) { if ("男".equals(person.getSex())&&person.getAge()<20){ temp.add(person); } } //new 1 List<PersonModel> collect = data .stream() .filter(person -> { if ("男".equals(person.getSex())&&person.getAge()<20){ return true; } return false; }) .collect(toList()); //new 2 List<PersonModel> collect1 = data .stream() .filter(person -> ("男".equals(person.getSex())&&person.getAge()<20)) .collect(toList()); }
distinct()
去除重複元素,這個方法是經過類的 equals 方法來判斷兩個元素是否相等的api
如例子中的 Person 類,須要先定義好 equals 方法,否則相似[Person{name='jack', age=20}, Person{name='jack', age=20}]
這樣的狀況是不會處理的數據結構
參考:https://blog.csdn.net/haiyoung/article/details/80934467
limit(long n)
limit: 對一個Stream進行截斷操做,獲取其前N個元素,若是原Stream中包含的元素個數小於N,那就獲取其全部的元素;app
返回前 n 個元素 list = list.stream() .limit(2) .collect(toList()); 打印輸出 [Person{name='jack', age=20}, Person{name='mike', age=25}]
skip方法 :
skip: 返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,若是原Stream中包含的元素個數小於N,那麼返回空Stream;ide
Map
- map生成的是個一對一映射,for的做用
- 比較經常使用
/** * 取出全部的用戶名字 */ public static void getUserNameList(){ List<PersonModel> data = Data.getData(); //old List<String> list=new ArrayList<>(); for (PersonModel persion:data) { list.add(persion.getName()); } System.out.println(list); //new 1 List<String> collect = data.stream().map(person -> person.getName()).collect(toList()); System.out.println(collect); //new 2 List<String> collect1 = data.stream().map(PersonModel::getName).collect(toList()); System.out.println(collect1); //new 3 List<String> collect2 = data.stream().map(person -> { System.out.println(person.getName()); return person.getName(); }).collect(toList()); }
filter 與 map 同時使用: 函數式編程
List<String> collect = users.stream().filter(item -> { if (!StringUtils.isEmpty(item.getUserName())) { return true; } return false; }).map(item -> item.getUserName()).collect(Collectors.toList());
FlatMap
-
顧名思義,跟map差很少,更深層次的操做函數
-
但仍是有區別的測試
-
map和flat返回值不一樣spa
-
Map 每一個輸入元素,都按照規則轉換成爲另一個元素。
還有一些場景,是一對多映射關係的,這時須要 flatMap。 -
Map一對一
-
Flatmap一對多
-
map和flatMap的方法聲明是不同的
-
- <r> Stream<r> map(Function mapper);
-
- <r> Stream<r> flatMap(Function> mapper);
-
map和flatMap的區別:我我的認爲,flatMap的能夠處理更深層次的數據,入參爲多個list,結果能夠返回爲一個list,而map是一對一的,入參是多個list,結果返回必須是多個list。通俗的說,若是入參都是對象,那麼flatMap能夠操做對象裏面的對象,而map只能操做第一層。
public static void flatMapString() { List<PersonModel> data = Data.getData(); //返回類型不同 List<String> collect = data.stream() .flatMap(person -> Arrays.stream(person.getName().split(" "))).collect(toList()); List<Stream<String>> collect1 = data.stream() .map(person -> Arrays.stream(person.getName().split(" "))).collect(toList()); //用map實現 List<String> collect2 = data.stream() .map(person -> person.getName().split(" ")) .flatMap(Arrays::stream).collect(toList()); //另外一種方式 List<String> collect3 = data.stream() .map(person -> person.getName().split(" ")) .flatMap(str -> Arrays.asList(str).stream()).collect(toList()); }
map轉list:
Map<String, List<ProgrammeResult>> projectGroups = programmeResults.stream().collect(Collectors.groupingBy(ProgrammeResult::getProjectId)); List<ProgrammeResult> fhSuccessResult = projectGroups.entrySet().stream().flatMap(item -> item.getValue().stream()).collect(Collectors.toList());
Collect
- collect在流中生成列表,map,等經常使用的數據結構
- toList()
- toSet()
- toMap()
/** * toList */ public static void toListTest(){ List<PersonModel> data = Data.getData(); List<String> collect = data.stream() .map(PersonModel::getName) .collect(Collectors.toList()); } /** * toSet */ public static void toSetTest(){ List<PersonModel> data = Data.getData(); Set<String> collect = data.stream() .map(PersonModel::getName) .collect(Collectors.toSet()); } /** * toMap */ public static void toMapTest(){ List<PersonModel> data = Data.getData(); Map<String, Integer> collect = data.stream() .collect( Collectors.toMap(PersonModel::getName, PersonModel::getAge) ); data.stream() .collect(Collectors.toMap(per->per.getName(), value->{ return value+"1"; })); } /** * 指定類型 */ public static void toTreeSetTest(){ List<PersonModel> data = Data.getData(); TreeSet<PersonModel> collect = data.stream() .collect(Collectors.toCollection(TreeSet::new)); System.out.println(collect); } /** * 分組 */ public static void toGroupTest(){ List<PersonModel> data = Data.getData(); Map<Boolean, List<PersonModel>> collect = data.stream() .collect(Collectors.groupingBy(per -> "男".equals(per.getSex()))); System.out.println(collect); } /** * 分隔 */ public static void toJoiningTest(){ List<PersonModel> data = Data.getData(); String collect = data.stream() .map(personModel -> personModel.getName()) .collect(Collectors.joining(",", "{", "}")); System.out.println(collect); }
groupingBy 分組
groupingBy 用於將數據分組,最終返回一個 Map 類型
Map<Integer, List<Person>> map = list.stream().collect(groupingBy(Person::getAge));
例子中咱們按照年齡 age 分組,每個 Person 對象中年齡相同的歸爲一組
另外能夠看出,Person::getAge
決定 Map 的鍵(Integer 類型),list 類型決定 Map 的值(List)
2.收集對象實體自己
- 在開發過程當中咱們也須要有時候對本身的list中的實體按照其中的一個字段進行分組(好比 id ->List),這時候要設置map的value值是實體自己。
public Map<Long, Account> getIdAccountMap(List<Account> accounts) { return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account)); }
account -> account是一個返回自己的lambda表達式,其實還可使用Function接口中的一個默認方法 Function.identity(),這個方法返回自身對象,更加簡潔
重複key的狀況。
在list轉爲map時,做爲key的值有可能重複,這時候流的處理會拋出個異常:Java.lang.IllegalStateException:Duplicate key。這時候就要在toMap方法中指定當key衝突時key的選擇。(這裏是選擇第二個key覆蓋第一個key)
public Map<String, Account> getNameAccountMap(List<Account> accounts) { return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2)); }
分組後統計每一個組的數量:
Map<Integer, Long> items = list.stream().collect(Collectors.groupingBy(User::getUserName,Collectors.counting()));
多級分組
groupingBy 能夠接受一個第二參數實現多級分組:
Map<Integer, Map<T, List<Person>>> map = list.stream().collect(groupingBy(Person::getAge, groupBy(...)));
partitioningBy 分區
分區與分組的區別在於,分區是按照 true 和 false 來分的,所以partitioningBy 接受的參數的 lambda 也是 T -> boolean
根據年齡是否小於等於20來分區 Map<Boolean, List<Person>> map = list.stream() .collect(partitioningBy(p -> p.getAge() <= 20)); 打印輸出 { false=[Person{name='mike', age=25}, Person{name='tom', age=30}], true=[Person{name='jack', age=20}] }
【統計】
List<User> users = User.getUsers(); int sum = users.stream().mapToInt(User::getUserAge).sum();//求和 System.out.println("sum==" + sum); int max = users.stream().mapToInt(User::getUserAge).max().getAsInt();//最大 System.out.println("max==" + max); int min = users.stream().mapToInt(User::getUserAge).min().getAsInt();//最小 System.out.println("min==" + min); Double average = users.stream().mapToInt(User::getUserAge).average().getAsDouble();//平均值 System.out.println("average==" + average); long count = users.stream().mapToInt(User::getUserAge).count(); // 獲得元素個數 System.out.println("count===" + count);
【參數匹配】
// allMatch 檢測是否所有知足指定的參數行爲 boolean b = users.stream().allMatch(User->User.getUserAge()>5); System.out.println("allMatch,檢測是否所有都知足指定的參數行爲:"+b); // anyMatch 檢測是否存在一個或者多個知足指定的參數行爲 boolean any = users.stream().anyMatch(User->User.getUserAge()>5); System.out.println("anyMatch,檢測是否存在一個或多個知足指定的參數行爲:"+any); // nonMatch 檢測是否不存在知足指定行爲的元素 boolean non = users.stream().noneMatch(User->User.getUserAge()>5); System.out.println("檢測是否不存在知足指定行爲的元素:"+non);
參考博客:
https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/
https://www.jianshu.com/p/9fe8632d0bc2
https://cloud.tencent.com/developer/article/1187833
https://www.concretepage.com/java/jdk-8/java-8-distinct-example