Java8實戰(zhàn)之Stream

2022-12-13 15:06:36 來源:51CTO博客

Java8實戰(zhàn)之Stream

前言

在前面一個小節(jié)中,我們已經(jīng)學習了行為參數(shù)化以及Lambda表達式,通過Lambda表達式,可以使得代碼更加簡潔,尤其是當一個方法只需要使用一次的時候,然而,如果Java8中只有Lambda表達式的話,那還是不足以讓人感到興奮的,個人感覺,Java8中最有意思,也是最方便的功能,莫過于??Stream??了

Stream初窺

??Stream??可以翻譯為流,實際上其操作也是,流操作是Java8中引入的新功能,提供了更加強大的數(shù)據(jù)迭代處理方式,通過流式寫法,提供了簡潔的語法,主要注意的是??Stream??需要配合Lambda表達式來使用,這更加體現(xiàn)了行為參數(shù)化的思想,Java8通過將既定的操作封裝好,同時,將對應的具體行為留給用戶,極大地提高了操作的效率。

??Stream??的出現(xiàn),可以說是用于替代傳統(tǒng)的容器操作的,在傳統(tǒng)的容器操作中,當需要對容器中的某些元素進行操作的時候,我們需要迭代容器,然后篩選出合適的對象,然后再將其存放到另外的容器中,從上面的描述中,可以看到,其中的很大一部分操作:迭代容器,篩選對象,重新存放基本都是固定的,而每次都進行手動操作,顯然是比較繁瑣的,??Stream??則提供了更加便捷的操作,只需要通過對應的操作模式,然后給出對應的條件,即可實現(xiàn)對既定元素的操作。


【資料圖】

為了下面的操作方便,我們先構造需要的元素

// User對象class User {    private Integer id;    private String name;    private Integer age;    // 省略set,get,toString方法}// 構造數(shù)據(jù)public static List generateUserData() {    Random random = new Random();    List users = new ArrayList<>();    for (int i = 0; i < 1000; i++) {        users.add(new User(i, "user" + i, random.nextInt(100)));    }    return users;}

假設現(xiàn)在有一個場景,我們需要從上面的列表中選取年齡大于20歲的對象,在傳統(tǒng)的容器操作中,一般我們會這樣操作

public List getUserOlderThan20() {    List users = generateUserData();    List result = new ArrayList<>();    for (User user : users) {        if (user.getAge() > 20 ) {            result.add(user);        }    }    return result;}

而在Java8中,我們可以用更加簡潔的方式來實現(xiàn)上面的操作

public List getUserOlderThan20() {    List users = generateUserData();    List result = users.stream()            .filter(user -> user.getAge() > 20)            .collect(Collectors.toList());    return result;}

或者上面的案例看上去并沒有那么有優(yōu)勢,那么我們來看下下面的案例,根據(jù)年齡對用戶進行分組,年齡在1-30為年輕人,31-60為中年人,60以上為老年人(例子例子,沒有實際價值)

傳統(tǒng)的操作,我們需要如下操作

public void groupUser() {    List users = generateUserData();    Map> userGroup = new HashMap<>();    for (User user : users) {        if (user.getAge() > 0 && user.getAge() <= 30) {            List young = userGroup.get("young");            if (young == null) {                young = new ArrayList<>();                userGroup.put("young", young);            }            userGroup.get("young").add(user);        }else if (user.getAge() <= 60) {            List middle = userGroup.get("middle");            if (middle == null) {                middle = new ArrayList<>();                userGroup.put("middle", middle);            }            userGroup.get("middle").add(user);        }else {            List old = userGroup.get("old");            if (old == null) {                old = new ArrayList<>();                userGroup.put("old", old);            }            userGroup.get("old").add(user);        }    }    System.out.println(userGroup);}

可以看到,上面的操作還是挺繁瑣的,而且比較容易出錯,而在Java8中,我們則可以采用如下操作

public void testStream() {    List users = generateUserData();    Map> result = users.stream()            .collect(Collectors.groupingBy(                            user -> {                                if (user.getAge() > 0 && user.getAge() <= 30) {                                    return "young";                                } else if (user.getAge() <= 60) {                                    return "middle";                                } else {                                    return "old";                                }}                            ));    System.out.println(result);}

可以看到,代碼量以及自描述性的對比還是挺明顯的,??Stream??配合??Lambda??表達式,可以使得之前比較繁瑣的容器操作,變得非常簡單,而且,代碼本身的自解釋性也更強

Stream操作

在前面我們已經(jīng)見識到了??Stream??本身的特點--流式操作以及方便性,接下來我們來詳細學習??Stream??的用法。

??Stream??的操作可以分為兩種,一種是中間操作,例如前面的??filter()??操作,一種是結束操作,例如前面的??collect()??操作,每一個中間操作,都返回一個??Stream??,經(jīng)過本次處理之后的??Stream??,結束操作則產(chǎn)生終結,其結果要么是數(shù)字,要么是字符串,要么是集合等等,總之就不再是??Stream??,也就是說,一個??Stream??可以有多個中間操作,但只能有一個結束操作

中間操作

比較常用的幾種中間操作列舉如下,更多的內容參考API即可

??filter()??,過濾操作,入?yún)??Predicate predicate????limit()??,限制操作,入?yún)??long maxSize????skip()??,跳過操作,入?yún)??long n????distinct()??,去重操作,沒有入?yún)ⅲ讓邮褂玫氖??Set??進行去重??sorted()??,排序操作,可以傳入自定義的比較器??Comparator comparator????peek()??,檢查操作,用于調試操作,入?yún)??Consumer action???map()??,將Stream中的元素映射為其他元素,入?yún)??Function mapper???mapToDouble()??,將Stream轉為??DoubleStream??,避免裝箱機制所帶來的開銷??mapToLong()??,將Stream轉為??LongStream??,避免裝箱機制所帶來的開銷??mapToInt()??,將Stream轉為??IntStream??,避免裝箱機制所帶來的開銷??flatMap()??,將多個Stream轉為一個,注意與??map()??的區(qū)別,入?yún)??Function> mapper??

結束操作

比較常用的幾個結束操作列舉如下,更多的內容參考API即可

??count()??,統(tǒng)計元素個數(shù)??forEach()??,對每個元素執(zhí)行操作,入?yún)??Consumer action????findFirst()??,獲取第一個元素??findAny()??,獲取任意一個元素??anyMatch()??,檢查元素是否至少有一個匹配,入?yún)??Predicate predicate????allMatch()??,檢查所有元素是否都匹配,入?yún)??Predicate predicate???collect()??,將所有內容收集起來,入?yún)??Collector collector??,JDK中提供了眾多的??Collector??的實現(xiàn),所以,基本上不用自己實現(xiàn)?groupingBy()??,將內容進行分組,有三個不同的版本??groupingBy(Function classifier)??,僅能進行一次分組??groupingBy(Function classifier, Collector downstream)??,注意第二個參數(shù)可以是另一個??Collector??,也就是說,可以通過多次的復合,達到多次分組,或者分組后再進行其他的操作??groupingBy(Function classifier,Supplier mapFactory, Collector downstream)??,自己提供一個容器,而不是使用默認的容器??counting()??,等價于前面的??Stream.count()????partitioningBy()??精簡版的??groupingBy()??,僅能支持??true??、??false??兩種分組??joining()??,字符串連接,需要注意,如果Stream的內容本身不是字符串流,則需要先??map()??操作一下,將其轉為字符串流,可以指定分隔符,前綴,后綴??toList()??,將結果合并為List??toSet()??,將結果合并為Set??toMap()??,將結果轉為Map??toConcurrentMap()??,將結果轉為并發(fā)Map?reduce()??,根據(jù)條件合并結果,可以說,上面的所有結束操作,基本上都可以通過??reduce()??來實現(xiàn),??reduce??有三個不同形式的參數(shù),當JDK所提供的合并操作不滿足需求時,可以通過??reduce??來實現(xiàn)自定義的合并操作??T reduce(T identity, BinaryOperator accumulator)????Optional reduce(BinaryOperator accumulator)???? U reduce(U identity, BiFunction accumulator, BinaryOperator combiner)??

Stream操作實例

為了更好地理解上面的內容,我們通過幾個小例子來實際操作一下

// 打印出年齡在30歲以上的所有用戶    users.stream()        .filter(user -> user.getAge() > 30)        .forEach(System.out::println);        // 如果換成 .count(),則是統(tǒng)計用戶的個數(shù)    // 分組并且統(tǒng)計各個分組的人數(shù)    Map collect = users.stream()                .collect(groupingBy(user -> {                    if (user.getAge() <= 30) {                        return "young";                    } else if (user.getAge() <= 60) {                        return "middle";                    } else {                        return "old";                    }                }, counting()));        // 分組并且去重    Map> collect = users.stream()                .collect(groupingBy(user -> {                    if (user.getAge() <= 30) {                        return "young";                    } else if (user.getAge() <= 60) {                        return "middle";                    } else {                        return "old";                    }                }, toSet()));

關于Stream的介紹,大致就到這里了,為了更好地掌握Stream,需要在實際使用中多加練習,多加研究才是

總結

本小節(jié)主要學習了Stream的內容,通過對比Stream與傳統(tǒng)的Collection操作,可以看出,通過Stream來操作容器,代碼將變得更加簡潔,而且,其可閱讀行也更強,出錯的概率也會更低,畢竟不用再自己關心迭代的過程,最后,通過幾個簡單的小例子,展示了Stream中兩種不同的操作,中間操作以及結束操作,當然,關于Stream的更多內容,還是需要在實際使用中不斷發(fā)現(xiàn),不斷研究,加油。

標簽: 我們需要 也就是說

上一篇:Linux:rinetd的安裝部署以及端口轉發(fā)
下一篇:天天觀天下!Awk教程?