java1.8新特性之stream流式算法

在Java1.8以前尚未stream流式算法的時候,咱們要是在一個放有多個User對象的list集合中,將每一個User對象的主鍵ID取出,組合成一個新的集合,首先想到的確定是遍歷,以下:java

List<Long> userIdList = new ArrayList<>();
for (User user: list) {
     userIdList.add(user.id);
}

或者在1.8有了lambda表達式之後,咱們會這樣寫:算法

List<Long> userIdList = new ArrayList<>();
list.forEach(user -> list.add(user.id));

在有了stream以後,咱們還能夠這樣寫:api

List<Long> userIdList = list.stream().map(User::getId).collect(Collectors.toList());  

一行代碼直接搞定,是否是很方便呢。那麼接下來。咱們就一塊兒看一下stream這個流式算法的新特性吧。數組

由上面的例子能夠看出,java8的流式處理極大的簡化了對於集合的操做,實際上不光是集合,包括數組、文件等,只要是能夠轉換成流,咱們均可以藉助流式處理,相似於咱們寫SQL語句同樣對其進行操做。java8經過內部迭代來實現對流的處理,一個流式處理能夠分爲三個部分:轉換成流、中間操做、終端操做。以下圖:app

以集合爲例,一個流式處理的操做咱們首先須要調用stream()函數將其轉換成流,而後再調用相應的中間操做達到咱們須要對集合進行的操做,好比篩選、轉換等,最後經過終端操做對前面的結果進行封裝,返回咱們須要的形式。函數

這裏咱們先建立一個User實體類:this

class User{
private Long id;       //主鍵id
private String name;   //姓名
private Integer age;   //年齡
private String school; //學校
public User(Long id, String name, Integer age, String school) {
   this.id = id;
   this.name = name;
   this.age = age;
   this.school = school;
}
此處省略get、set方法。。。

初始化:spa

List<User> list = new ArrayList<User>(){
    {
        add(new User(1l,"張三",10, "清華大學"));
        add(new User(2l,"李四",12, "清華大學"));
        add(new User(3l,"王五",15, "清華大學"));
        add(new User(4l,"趙六",12, "清華大學"));
        add(new User(5l,"田七",25, "北京大學"));
        add(new User(6l,"小明",16, "北京大學"));
        add(new User(7l,"小紅",14, "北京大學"));
        add(new User(8l,"小華",14, "浙江大學"));
        add(new User(9l,"小麗",17, "浙江大學"));
        add(new User(10l,"小何",10, "浙江大學"));
    }
};

1. 過濾  .net

1.1 filtercode

咱們但願過濾賽選處全部學校是清華大學的user:

System.out.println("學校是清華大學的user");
List<User> userList1 = list.stream().filter(user -> "清華大學".equals(user.getSchool())).collect(Collectors.toList());
userList1.forEach(user -> System.out.print(user.name + '、'));

控制檯輸出結果爲:

學校是清華大學的user
張3、李4、王5、趙6、

1.2 distinct

去重,咱們但願獲取全部user的年齡(年齡不重複)

System.out.println("全部user的年齡集合");
List<Integer> userAgeList = list.stream().map(User::getAge).distinct().collect(Collectors.toList());
System.out.println("userAgeList = " + userAgeList);

map在下面會講到,如今主要是看distinct的用法,輸出結果以下:

全部user的年齡集合
userAgeList = [10, 12, 15, 25, 16, 14, 17]

1.3 limit 

返回前n個元素的流,當集合的長度小於n時,則返回全部集合。

如獲取年齡是偶數的前2名user:

System.out.println("年齡是偶數的前兩位user");
List<User> userList3 = list.stream().filter(user -> user.getAge() % 2 == 0).limit(2).collect(Collectors.toList());
userList3.forEach(user -> System.out.print(user.name + '、'));

輸出結果爲:

年齡是偶數的前兩位user
張3、李4、

1.4 sorted

排序,如如今我想將全部user按照age從大到小排序:

System.out.println("按年齡從大到小排序");
List<User> userList4 = list.stream().sorted((s1,s2) -> s2.age - s1.age).collect(Collectors.toList());
userList4.forEach(user -> System.out.print(user.name + '、'));

輸出結果爲:

按年齡從大到小排序
田7、小麗、小明、王5、小紅、小華、李4、趙6、張3、小何、

1.5 skip

跳過n個元素後再輸出

如輸出list集合跳過前兩個元素後的list

System.out.println("跳過前面兩個user的其餘全部user");
List<User> userList5 = list.stream().skip(2).collect(Collectors.toList());
userList5.forEach(user -> System.out.print(user.name + '、'));

輸出結果爲:

跳過前面兩個user的其餘全部user
王5、趙6、田7、小明、小紅、小華、小麗、小何、

2 映射

2.1 map

就是講user這個幾個精簡爲某個字段的集合

如我如今想知道學校是清華大學的全部學生的姓名:

System.out.println("學校是清華大學的user的名字");
List<String> userList6 = list.stream().filter(user -> "清華大學".equals(user.school)).map(User::getName).collect(Collectors.toList());
userList6.forEach(user -> System.out.print(user + '、'));  

輸出結果以下:

學校是清華大學的user的名字
張3、李4、王5、趙6、

除了上面這類基礎的map,java8還提供了mapToDouble(ToDoubleFunction<? super T> mapper),mapToInt(ToIntFunction<? super T> mapper),mapToLong(ToLongFunction<? super T> mapper),這些映射分別返回對應類型的流,java8爲這些流設定了一些特殊的操做,好比查詢學校是清華大學的user的年齡總和:

System.out.println("學校是清華大學的user的年齡總和");
int userList7 = list.stream().filter(user -> "清華大學".equals(user.school)).mapToInt(User::getAge).sum();
System.out.println( "學校是清華大學的user的年齡總和爲: "+userList7);

輸出結果爲:

學校是清華大學的user的年齡總和
學校是清華大學的user的年齡總和爲: 49

1.2 flatMap

flatMap與map的區別在於 flatMap是將一個流中的每一個值都轉成一個個流,而後再將這些流扁平化成爲一個流 。舉例說明,假設咱們有一個字符串數組String[] strs = {"hello", "world"};,咱們但願輸出構成這一數組的全部非重複字符,那麼咱們用map和flatMap 實現以下:

String[] strings = {"Hello", "World"};
List l11 = Arrays.stream(strings).map(str -> str.split("")).map(str2->Arrays.stream(str2)).distinct().collect(Collectors.toList());
List l2 = Arrays.asList(strings).stream().map(s -> s.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
System.out.println(l11.toString());
System.out.println(l2.toString());  

輸出結果以下:

[java.util.stream.ReferencePipeline$Head@4c203ea1, java.util.stream.ReferencePipeline$Head@27f674d]
[H, e, l, o, W, r, d]  

由上咱們能夠看到使用map並不能實現咱們如今想要的結果,而flatMap是能夠的。這是由於在執行map操做之後,咱們獲得是一個包含多個字符串(構成一個字符串的字符數組)的流,此時執行distinct操做是基於在這些字符串數組之間的對比,因此達不到咱們但願的目的;flatMap將由map映射獲得的Stream<String[]>,轉換成由各個字符串數組映射成的流Stream<String>,再將這些小的流扁平化成爲一個由全部字符串構成的大流Steam<String>,從而可以達到咱們的目的。

3 查找

3.1 allMatch

用於檢測是否所有都知足指定的參數行爲,若是所有知足則返回true,例如咱們判斷是否全部的user年齡都大於9歲,實現以下:

System.out.println("判斷是否全部user的年齡都大於9歲");
Boolean b = list.stream().allMatch(user -> user.age >9);
System.out.println(b);

輸出結果爲:

判斷是否全部user的年齡都大於9歲
true

3.2 anyMatch

anyMatch則是檢測是否存在一個或多個知足指定的參數行爲,若是知足則返回true,例如判斷是否有user的年齡大於15歲,實現以下:

System.out.println("判斷是否有user的年齡是大於15歲");
Boolean bo = list.stream().anyMatch(user -> user.age >15);
System.out.println(bo);

輸出結果爲:

判斷是否有user的年齡是大於15歲
true

3.3 noneMatch  

noneMatch用於檢測是否不存在知足指定行爲的元素,若是不存在則返回true,例如判斷是否不存在年齡是15歲的user,實現以下:

System.out.println("判斷是否不存在年齡是15歲的user");
Boolean boo = list.stream().noneMatch(user -> user.age == 15);
System.out.println(boo);

輸出結果以下:

判斷是否不存在年齡是15歲的user
false

3.4 findFirst

findFirst用於返回知足條件的第一個元素,好比返回年齡大於12歲的user中的第一個,實現以下:

System.out.println("返回年齡大於12歲的user中的第一個");
Optional<User> first = list.stream().filter(u -> u.age > 10).findFirst();
User user = first.get();
System.out.println(user.toString());

輸出結果以下:

返回年齡大於12歲的user中的第一個
User{id=2, name='李四', age=12, school='清華大學'}

3.5 findAny

findAny相對於findFirst的區別在於,findAny不必定返回第一個,而是返回任意一個,好比返回年齡大於12歲的user中的任意一個:

System.out.println("返回年齡大於12歲的user中的任意一個");
Optional<User> anyOne = list.stream().filter(u -> u.age > 10).findAny();
User user2 = anyOne.get();
System.out.println(user2.toString());

輸出結果以下:

返回年齡大於12歲的user中的任意一個
User{id=2, name='李四', age=12, school='清華大學'}

4 歸約

4.1 reduce

如今個人目標不是返回一個新的集合,而是但願對通過參數化操做後的集合進行進一步的運算,那麼咱們可用對集合實施歸約操做。java8的流式處理提供了reduce方法來達到這一目的。

好比我如今要查出學校是清華大學的全部user的年齡之和:

//前面用到的方法
Integer ages = list.stream().filter(student -> "清華大學".equals(student.school)).mapToInt(User::getAge).sum();
System.out.println(ages);
System.out.println("歸約 - - 》 start ");
Integer ages2 = list.stream().filter(student -> "清華大學".equals(student.school)).map(User::getAge).reduce(0,(a,c)->a+c);
Integer ages3 = list.stream().filter(student -> "清華大學".equals(student.school)).map(User::getAge).reduce(0,Integer::sum);
Integer ages4 = list.stream().filter(student -> "清華大學".equals(student.school)).map(User::getAge).reduce(Integer::sum).get();
System.out.println(ages2);
System.out.println(ages3);
System.out.println(ages4);
System.out.println("歸約 - - 》 end ");

輸出結果爲:

49
歸約 - - 》 start
49
49
49
歸約 - - 》 end

5 收集

前面利用collect(Collectors.toList())是一個簡單的收集操做,是對處理結果的封裝,對應的還有toSet、toMap,以知足咱們對於結果組織的需求。這些方法均來自於java.util.stream.Collectors,咱們能夠稱之爲收集器。

收集器也提供了相應的歸約操做,可是與reduce在內部實現上是有區別的,收集器更加適用於可變容器上的歸約操做,這些收集器廣義上均基於Collectors.reducing()實現。

5.1 counting 

計算個數

如我如今計算user的總人數,實現以下:

System.out.println("user的總人數");
long COUNT = list.stream().count();//簡化版本
long COUNT2 = list.stream().collect(Collectors.counting());//原始版本
System.out.println(COUNT);
System.out.println(COUNT2);

輸出結果爲:

user的總人數
10
10

5.2 maxBy、minBy

計算最大值和最小值

如我如今計算user的年齡最大值和最小值:

System.out.println("user的年齡最大值和最小值");
Integer maxAge =list.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge())).get().age;
Integer maxAge2 = list.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge))).get().age;
Integer minAge = list.stream().collect(Collectors.minBy((S1,S2) -> S1.getAge()- S2.getAge())).get().age;
Integer minAge2 = list.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge))).get().age;
System.out.println("maxAge = " + maxAge);
System.out.println("maxAge2 = " + maxAge2);
System.out.println("minAge = " + minAge);
System.out.println("minAge2 = " + minAge2);

輸出結果爲:

user的年齡最大值
maxAge = 25
maxAge2 = 25
minAge = 10
minAge2 = 10

5.3 summingInt、summingLong、summingDouble

總和

如計算user的年齡總和:

System.out.println("user的年齡總和");
Integer sumAge =list.stream().collect(Collectors.summingInt(User::getAge));
System.out.println("sumAge = " + sumAge);

輸出結果爲:

user的年齡總和
sumAge = 145

5.4 averageInt、averageLong、averageDouble

平均值

如計算user的年齡平均值:

System.out.println("user的年齡平均值");
double averageAge = list.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println("averageAge = " + averageAge);

輸出結果爲:

user的年齡平均值
averageAge = 14.5

5.5 summarizingInt、summarizingLong、summarizingDouble

一次性查詢元素個數、總和、最大值、最小值和平均值

System.out.println("一次性獲得元素個數、總和、均值、最大值、最小值");
long l1 = System.currentTimeMillis();
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
long l111 = System.currentTimeMillis();
System.out.println("計算這5個值消耗時間爲" + (l111-l1));
System.out.println("summaryStatistics = " + summaryStatistics);

輸出結果爲:

一次性獲得元素個數、總和、均值、最大值、最小值
計算這5個值消耗時間爲3
summaryStatistics = IntSummaryStatistics{count=10, sum=145, min=10, average=14.500000, max=25}

5.6 joining

字符串拼接

如輸出全部user的名字,用「,」隔開

System.out.println("字符串拼接");
String names = list.stream().map(User::getName).collect(Collectors.joining(","));
System.out.println("names = " + names);

輸出結果爲:

字符串拼接
names = 張三,李四,王五,趙六,田七,小明,小紅,小華,小麗,小何

5.7 groupingBy

分組

如將user根據學校分組、先按學校分再按年齡分、每一個大學的user人數、每一個大學不一樣年齡的人數:

System.out.println("分組");
Map<String, List<User>> collect1 = list.stream().collect(Collectors.groupingBy(User::getSchool));
Map<String, Map<Integer, Long>> collect2 = list.stream().collect(Collectors.groupingBy(User::getSchool, Collectors.groupingBy(User::getAge, Collectors.counting())));
Map<String, Map<Integer, Map<String, Long>>> collect4 = list.stream().collect(Collectors.groupingBy(User::getSchool, Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName,Collectors.counting()))));
Map<String, Long> collect3 = list.stream().collect(Collectors.groupingBy(User::getSchool, Collectors.counting()));
System.out.println("collect1 = " + collect1);
System.out.println("collect2 = " + collect2);
System.out.println("collect3 = " + collect3);
System.out.println("collect4 = " + collect4);

輸出結果爲:

分組
collect1 = {浙江大學=[User{id=8, name='小華', age=14, school='浙江大學'}, User{id=9, name='小麗', age=17, school='浙江大學'}, User{id=10, name='小何', age=10, school='浙江大學'}], 北京大學=[User{id=5, name='田七', age=25, school='北京大學'}, User{id=6, name='小明', age=16, school='北京大學'}, User{id=7, name='小紅', age=14, school='北京大學'}], 清華大學=[User{id=1, name='張三', age=10, school='清華大學'}, User{id=2, name='李四', age=12, school='清華大學'}, User{id=3, name='王五', age=15, school='清華大學'}, User{id=4, name='趙六', age=12, school='清華大學'}]}
collect2 = {浙江大學={17=1, 10=1, 14=1}, 北京大學={16=1, 25=1, 14=1}, 清華大學={10=1, 12=2, 15=1}}
collect3 = {浙江大學=3, 北京大學=3, 清華大學=4}
collect4 = {浙江大學={17={小麗=1}, 10={小何=1}, 14={小華=1}}, 北京大學={16={小明=1}, 25={田七=1}, 14={小紅=1}}, 清華大學={10={張三=1}, 12={李四=1, 趙六=1}, 15={王五=1}}}

5.8 partitioningBy  

分區,分區能夠看作是分組的一種特殊狀況,在分區中key只有兩種狀況:true或false,目的是將待分區集合按照條件一分爲二,java8的流式處理利用ollectors.partitioningBy()方法實現分區。

如按照是不是清華大學的user將左右user分爲兩個部分:

System.out.println("分區");
Map<Boolean, List<User>> collect5 = list.stream().collect(Collectors.partitioningBy(user1 -> "清華大學".equals(user1.school)));
System.out.println("collect5 = " + collect5);

輸出結果爲:

分區
collect5 = {false=[User{id=5, name='田七', age=25, school='北京大學'}, User{id=6, name='小明', age=16, school='北京大學'}, User{id=7, name='小紅', age=14, school='北京大學'}, User{id=8, name='小華', age=14, school='浙江大學'}, User{id=9, name='小麗', age=17, school='浙江大學'}, User{id=10, name='小何', age=10, school='浙江大學'}], true=[User{id=1, name='張三', age=10, school='清華大學'}, User{id=2, name='李四', age=12, school='清華大學'}, User{id=3, name='王五', age=15, school='清華大學'}, User{id=4, name='趙六', age=12, school='清華大學'}]}

 

關於Java1.8新特性之stream暫時只說這些,但願對你能有所幫助,謝謝!

參考博客:https://blog.csdn.net/papima/article/details/81808445

相關文章
相關標籤/搜索