Java 8 Stream實踐

前面的話】Java中的Stream於1.8版本析出,平時項目中也有用到,今天就係統的來實踐一下。下面借用重慶力帆隊伍中我我的比較喜歡的球員來操做一波,隊員的年齡爲了便於展現某些api作了調整,請不要太認真哦。java


壹. Stream理解

在java中咱們稱Stream爲『』,咱們常常會用流去對集合進行一些流水線的操做。stream就像工廠同樣,只須要把集合、命令還有一些參數灌輸到流水線中去,就能夠加工成得出想要的結果。這樣的流水線能大大簡潔代碼,減小操做。給我我的的感受相似JavaScript中的鏈式函數。sql

貳. Stream流程

原集合 —> 流 —> 各類操做(過濾、分組、統計) —> 終端操做

Stream流的操做流程通常都是這樣的,先將集合轉爲流,而後通過各類操做,好比過濾、篩選、分組、計算。最後的終端操做,就是轉化成咱們想要的數據,這個數據的形式通常仍是集合,有時也會按照需求輸出count計數。下文會一一舉例。數據庫

叄. API實踐

首先,定義一個用戶對象,包含姓名、年齡、id三個成員變量:api

package com.eelve.training.entity;

import lombok.*;

import javax.persistence.*;

/**
 * @ClassName User
 * @Description TDO
 * @Author zhao.zhilue
 * @Date 2019/6/28 15:21
 * @Version 1.0
 **/
@Data
@Entity
@Table(name = "user")
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(exclude={"id","name"})
public class User implements  Comparable<User>{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    /**
     * Link name.
     */
    @Column(name = "name", columnDefinition = "varchar(255) not null")
    private String name;

    @Column(name = "age")
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(User o) {
        return age.compareTo(o.getAge());
    }
}

而後在數據庫中插入測試數據,見下圖: streamDataSource數組

3.1過濾

1)filter 過濾(T-> boolean)

假如咱們要實現過濾出40歲如下的隊員,咱們能夠這樣來實現:app

@Test
    public void testUserStreamFilter(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().filter(user -> user.getAge() <= 40).collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

filter裏面,->箭頭後面跟着的是一個boolean值,能夠寫任何的過濾條件,就至關於sql中where後面的東西,換句話說,能用sql實現的功能這裏均可以實現 執行結果爲:ide

User(id=1, name=費爾南多, age=25)
User(id=2, name=費爾南迪尼奧, age=26)
User(id=3, name=卡爾德克, age=27)
User(id=4, name=阿德里安, age=28)
User(id=5, name=隋維傑, age=26)

2)distinct 去重

其用法和sql中的使用相似,假如咱們要實現過去除用重複年齡的隊員,咱們能夠這樣來實現:函數

@Test
    public void testUserDistinct(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().distinct().collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

執行結果爲:測試

User(id=1, name=費爾南多, age=25)
User(id=2, name=費爾南迪尼奧, age=26)
User(id=3, name=卡爾德克, age=27)
User(id=4, name=阿德里安, age=28)
User(id=6, name=克魯伊夫, age=43)

3)sorted排序

若是流中的元素的類實現了 Comparable 接口,即有本身的排序規則,那麼能夠直接調用 sorted() 方法對元素進行排序,如:this

@Override
    public int compareTo(User o) {
        return age.compareTo(o.getAge());
    }
@Test
    public void testUserStreamSorted(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().sorted().collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

反之, 須要調用 sorted((T, T) -> int) 實現 Comparator 接口。

@Test
    public void testUserStreamSortedWithComparator(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

執行結果爲:

User(id=1, name=費爾南多, age=25)
User(id=2, name=費爾南迪尼奧, age=26)
User(id=5, name=隋維傑, age=26)
User(id=3, name=卡爾德克, age=27)
User(id=4, name=阿德里安, age=28)
User(id=6, name=克魯伊夫, age=43)

4)limit() 返回前n個元素

若是想知道隊伍中年齡最小的就可使用下面來實現:

@Test
    public void testUserStreamLimit(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().limit(2).collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

執行結果爲:

User(id=1, name=費爾南多, age=25)
User(id=2, name=費爾南迪尼奧, age=26)

5)skip

它的用法和limit正好相反,是去除前面幾個元素。 假如咱們要去除前面兩個元素就可使用下面的方法來實現:

@Test
    public void testUserStreamSkip(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().skip(2).collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

執行結果爲:

User(id=3, name=卡爾德克, age=27)
User(id=4, name=阿德里安, age=28)
User(id=5, name=隋維傑, age=26)
User(id=6, name=克魯伊夫, age=43)

6)組合使用

以上的過濾函數物品們能夠組合來使用來實現咱們具體的需求,示例代碼以下:

@Test
    public void testUserStreamSortLimit(){
        List<User> userList = userMapper.getALL();
        List<User> resultList = userList.stream().sorted().limit(5).collect(Collectors.toList());
        for (User user :  resultList){
            System.out.println(user.toString());
        }
    }

這樣咱們就能夠獲得先排序後限制的結果:

User(id=1, name=費爾南多, age=25)
User(id=2, name=費爾南迪尼奧, age=26)
User(id=5, name=隋維傑, age=26)
User(id=3, name=卡爾德克, age=27)
User(id=4, name=阿德里安, age=28)

3.2 映射

1)map(T->R)

map是將T類型的數據轉爲R類型的數據,好比咱們想要設置一個新的list,存儲用戶全部的城市信息。

@Test
    public void testUserStreamMap(){
        List<User> userList = userMapper.getALL();
        List<Integer> resultList = userList.stream().map(User::getAge).distinct().collect(Collectors.toList());
        System.out.println(resultList.toString());
    }

這樣咱們能夠獲得全部年齡的樣本,執行結果爲:

[25, 26, 27, 28, 43]

2)flatMap(T -> Stream<R>)

將流中的每個元素 T 映射爲一個流,再把每個流鏈接成爲一個流。

@Test
    public void testStreamMap(){
        List<String> habitsList = new ArrayList<>();
        habitsList.add("唱歌,聽歌");
        habitsList.add("羽毛球,足球,爬山");
        habitsList = habitsList.stream().map(s -> s.split(",")).flatMap(Arrays::stream).collect(Collectors.toList());
        System.out.println(habitsList);
    }

執行結果爲:

[唱歌, 聽歌, 羽毛球, 足球, 爬山]

這裏原集合中的數據由逗號分割,使用split進行拆分後,獲得的是Stream<String[]>,字符串數組組成的流,要使用flatMap的Arrays::stream,將Stream<String[]>轉爲Stream<String>,而後把流相鏈接,組成了完整的唱歌, 聽歌, 羽毛球, 足球, 爬山。

3.3 查找

1)allMatch(T->boolean)

檢測是否所有知足參數行爲,假如咱們要檢測是否是全部隊員都是U21的球員:

@Test
    public void testUserStreamAllMatch(){
        List<User> userList = userMapper.getALL();
        boolean isNotU21 = userList.stream().allMatch(user -> user.getAge() >= 21);
        System.out.println("是否都不是U21球員:" + isNotU21);
    }

執行結果爲:

是否都不是U21球員:true

2)anyMatch(T->boolean)

檢測是否有任意元素知足給定的條件,好比,想知道是否有26歲的球員:

@Test
    public void testUserStreamAnyMatch(){
        List<User> userList = userMapper.getALL();
        boolean isAgeU26 = userList.stream().anyMatch(user -> user.getAge() == 26);
        System.out.println("是否有26歲的球員:" + isAgeU26);
    }

執行結果爲:

是否有26歲的球員:true

3)noneMatch(T -> boolean)

流中是否有元素匹配給定的 T -> boolean 條件。好比咱們要檢測是否含有U18的隊員:

@Test
    public void testUserStreamNoneMatch(){
        List<User> userList = userMapper.getALL();
        boolean isNotU18 = userList.stream().noneMatch(user -> user.getAge() <= 18);
        System.out.println("是否都不是U18球員:" + isNotU18);
    }

執行結果爲:

是否都不是U18球員:true

說明沒有U18的隊員。

4)findFirst( ):找到第一個元素

@Test
    public void testUserFindFirst(){
        List<User> userList = userMapper.getALL();
        Optional<User> firstUser = userList.stream().sorted().findFirst();
        System.out.println(firstUser.toString());
    }

執行結果爲:

Optional[User(id=1, name=費爾南多, age=25)]

5)findAny():找到任意一個元素

@Test
    public void testUserFindAny(){
        List<User> userList = userMapper.getALL();
        Optional<User> anytUser = userList.parallelStream().sorted().findAny();
        System.out.println(anytUser.toString());
    }

執行結果爲:

Optional[User(id=2, name=費爾南迪尼奧, age=26)]

3.4 概括計算

1)求隊員的總人數

@Test
    public void testUserCount(){
        List<User> userList = userMapper.getALL();
        long totalAge = userList.stream().collect(Collectors.counting());
        System.out.println("隊員人數爲:" + totalAge);
    }

執行結果爲:

隊員人數爲:6

2)獲得某一屬性的最大最小值

@Test
    public void testUserMaxAndMin(){
        List<User> userList = userMapper.getALL();
        Optional<User> userMaxAge = userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge)));
        System.out.println("年齡最大的隊員爲:" + userMaxAge.toString());

        Optional<User> userMinAge = userList.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge)));
        System.out.println("年齡最小的隊員爲:" + userMinAge.toString());
    }

執行結果爲:

年齡最大的隊員爲:Optional[User(id=6, name=克魯伊夫, age=43)]
年齡最小的隊員爲:Optional[User(id=1, name=費爾南多, age=25)]

3)求年齡總和是多少

@Test
    public void testUserSummingInt(){
        List<User> userList = userMapper.getALL();
        int totalAge = userList.stream().collect(Collectors.summingInt(User::getAge));
        System.out.println("年齡總和爲:" + totalAge);
    }

執行結果爲:

年齡總和爲:175

咱們常常會用BigDecimal來記錄金錢,假設想獲得BigDecimal的總和: // 得到列表對象金額, 使用reduce聚合函數,實現累加器 BigDecimal sum = myList.stream() .map(User::getMoney) .reduce(BigDecimal.ZERO,BigDecimal::add);

4)求年齡平均值

@Test
    public void testUserAveragingInt(){
        List<User> userList = userMapper.getALL();
        Double totalAge = userList.stream().collect(Collectors.averagingInt(User::getAge));
        System.out.println("平均年齡爲:" + totalAge);
    }

執行結果爲:

平均年齡爲:29.166666666666668

5)一次性獲得元素的個數、總和、最大值、最小值

@Test
    public void testUserSummarizingInt(){
        List<User> userList = userMapper.getALL();
        IntSummaryStatistics  statistics  = userList.stream().collect(Collectors.summarizingInt(User::getAge));
        System.out.println("年齡的統計結果爲:" + statistics );
    }

執行結果爲:

年齡的統計結果爲:IntSummaryStatistics{count=6, sum=175, min=25, average=29.166667, max=43}

6)字符串拼接

要將隊員的姓名連成一個字符串並用逗號分割。

@Test
    public void testUserJoining(){
        List<User> userList = userMapper.getALL();
        String  name  = userList.stream().map(User::getName).collect(Collectors.joining(","));
        System.out.println("全部的隊員名字:" + name );
    }

執行結果爲:

全部的隊員名字:費爾南多,費爾南迪尼奧,卡爾德克,阿德里安,隋維傑,克魯伊夫

3.5 分組

在數據庫操做中,咱們常常經過GROUP BY關鍵字對查詢到的數據進行分組,java8的流式處理也提供了分組的功能。使用Collectors.groupingBy來進行分組。

1)能夠根據隊員的年齡進行分組

@Test
    public void testUserGroupingBy(){
        List<User> userList = userMapper.getALL();
        Map<Integer, List<User>> ageMap  = userList.stream().collect(Collectors.groupingBy(User::getAge));
        for (Map.Entry<Integer,List<User>> entry :ageMap.entrySet()){
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
    }

執行結果爲:

key= 25 and value= [User(id=1, name=費爾南多, age=25)]
key= 26 and value= [User(id=2, name=費爾南迪尼奧, age=26), User(id=5, name=隋維傑, age=26)]
key= 43 and value= [User(id=6, name=克魯伊夫, age=43)]
key= 27 and value= [User(id=3, name=卡爾德克, age=27)]
key= 28 and value= [User(id=4, name=阿德里安, age=28)]

結果是一個map,key爲不重複的年齡,value爲屬於該年齡的隊員列表。已經實現了分組。另外咱們還能夠繼續分組獲得兩次分組的結果。

2)若是僅僅想統計各年齡的隊員個數是多少,並不須要對應的list

按年齡分組並統計人數:

@Test
    public void testUserGroupingByCount(){
        List<User> userList = userMapper.getALL();
        Map<Integer,Long> ageMap  = userList.stream().collect(Collectors.groupingBy(User::getAge,Collectors.counting()));
        for (Map.Entry<Integer,Long> entry :ageMap.entrySet()){
            System.out.println("隊員中" + entry.getKey() + "歲的隊員人數爲:" + entry.getValue());
        }
    }

執行結果爲:

隊員中25歲的隊員人數爲:1
隊員中26歲的隊員人數爲:2
隊員中43歲的隊員人數爲:1
隊員中27歲的隊員人數爲:1
隊員中28歲的隊員人數爲:1

3)partitioningBy 分區

分區與分組的區別在於,分區是按照 true 和 false 來分的,所以partitioningBy 接受的參數的 lambda 也是 T -> boolean

@Test
    public void testUserPartitioningBy (){
        List<User> userList = userMapper.getALL();
        Map<Boolean,List<User>> partitioningByMap  = userList.stream().collect(partitioningBy(user -> user.getAge() >= 30));
        for (Map.Entry<Boolean,List<User>> entry :partitioningByMap.entrySet()){
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
    }

執行結果爲:

key= false and value= [User(id=1, name=費爾南多, age=25), User(id=2, name=費爾南迪尼奧, age=26), User(id=3, name=卡爾德克, age=27), User(id=4, name=阿德里安, age=28), User(id=5, name=隋維傑, age=26)]
key= true and value= [User(id=6, name=克魯伊夫, age=43)]

寫在後面的話】留下stream的類實現的方法和依賴圖,前面的實踐也只是挑選了幾個比較經常使用的Api。

stream

相關文章
相關標籤/搜索