欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java8中Stream流式操作指南之入門篇

 更新時(shí)間:2022年02月16日 11:48:38   作者:湯圓  
Java8中有兩大最為重要的改變,第一個(gè)是Lambda?表達(dá)式,另外一個(gè)則是Stream?API(java.util.stream.*),下面這篇文章主要給大家介紹了Java8中Stream流式操作指南之入門篇的相關(guān)資料,需要的朋友可以參考下

簡(jiǎn)介

流式操作也叫做函數(shù)式操作,是Java8新出的功能

流式操作主要用來處理數(shù)據(jù)(比如集合),就像泛型也大多用在集合中一樣(看來集合這個(gè)小東西還是很關(guān)鍵的啊,哪哪都有它)

下面我們主要用例子來介紹下,流的基操(建議先看下lambda表達(dá)式篇,里面介紹的lambda表達(dá)式、函數(shù)式接口、方法引用等,下面會(huì)用到)

正文

1. 流是什么

流是一種以聲明性的方式來處理數(shù)據(jù)的API

什么是聲明性的方式?

就是只聲明,不實(shí)現(xiàn),類似抽象方法(多態(tài)性)

2. 老板,上栗子

下面我們舉個(gè)栗子,來看下什么是流式操作,然后針對(duì)這個(gè)栗子,引出后面的相關(guān)概念

需求篩選年齡大于1的貓(貓的1年≈人的5年),并按年齡遞增排序,最后提取名字單獨(dú)存放到列表中

public class BasicDemo {
   public static void main(String[] args) {
     // 以下貓的名字均為真名,非虛構(gòu)
       List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu"));
       // === 舊代碼 Java8之前 ===
       List<Cat> listTemp = new ArrayList<>();
       // 1. 篩選
       for(Cat cat: list){
           if(cat.getAge()>1){
               listTemp.add(cat);
          }
      }
       // 2. 排序
       listTemp.sort(new Comparator<Cat>() {
           @Override
           public int compare(Cat o1, Cat o2) {
               // 遞增排序
               return Integer.compare(o1.getAge(), o2.getAge());
          }
      });
       // 3. 提取名字
       List<String> listName = new ArrayList<>();
       for(Cat cat: listTemp){
           listName.add(cat.getName());
      }
       System.out.println(listName);
       
       // === 新代碼 Java8之后 ===
       List<String> listNameNew = list.stream()
        // 函數(shù)式接口 Predicate的 boolean test(T t)抽象方法
        .filter(cat -> cat.getAge() > 1)
         // lambda表達(dá)式的方法引用
        .sorted(Comparator.comparingInt(Cat::getAge))
        // 函數(shù)式接口 Funtion的 R apply(T t)抽象方法
        .map(cat-> cat.getName())
        // 收集數(shù)據(jù),把流轉(zhuǎn)為集合List
        .collect(Collectors.toList());
       System.out.println(listNameNew);
  }
}
class Cat{
   int age;
   String name;

   public Cat(int age, String name) {
       this.age = age;
       this.name = name;
  }
// 省略getter/setter
}

可以看到,用了流式操作,代碼簡(jiǎn)潔了很多(秒哇)

Q:有的官人可能會(huì)想,這跟前面lambda表達(dá)式的組合操作有點(diǎn)像啊。

A:你說對(duì)了,確實(shí)只是像,區(qū)別還是很大的。因?yàn)閘ambda表達(dá)式的組合操作其實(shí)還是屬于直接針對(duì)集合的操作;

文末會(huì)講到直接操作集合和流式操作的區(qū)別,這里先跳過

下面我們基于這個(gè)栗子,來分別介紹涉及到的知識(shí)點(diǎn)

3. 流的操作步驟

我們先忽略舊版的集合操作(后面介紹流和集合的區(qū)別時(shí)再說),先來介紹流的操作(畢竟流才是今天的主角嘛)

流的操作分三步走:創(chuàng)建流、中間操作、終端操作

流程如下圖:

這里我們要關(guān)注一個(gè)很重要的點(diǎn):

在終端操作開始之前,中間操作不會(huì)執(zhí)行任何處理,它只是聲明執(zhí)行什么操作;

你可以想象上面這個(gè)流程是一個(gè)流水線:我們這里做個(gè)簡(jiǎn)化處理

  • 目的:先告訴你,我們要加工瓶裝的水(先創(chuàng)建流,告訴你要處理哪些數(shù)據(jù))

  • 再針對(duì)這些瓶子和水,來搭建一個(gè)流水線:固定瓶子的夾具、裝水的水管、擰蓋子的爪子、裝箱的打包器(中間操作,聲明要執(zhí)行的操作)

  • 最后按下啟動(dòng)按鈕,流水線開始工作(終端操作,開始根據(jù)中間操作來處理數(shù)據(jù))

因?yàn)槊恳粋€(gè)中間操作都是返回一個(gè)流(Stream),這樣他們就可以一直組合下去(我好像吃到了什么東西?),但是他們的組合順序是不固定的,流會(huì)根據(jù)系統(tǒng)性能去選擇合適的組合順序

我們可以打印一些東西來看下:

List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu"));
List<String> listNameNew = list.stream()
.filter(cat -> {
   System.out.println("filter: " + cat);
   return cat.getAge() > 1;
})
.map(cat-> {
   System.out.println("map:" + cat);
   return cat.getName();
})
.collect(Collectors.toList());

輸出如下:

filter: Cat{age=1}
filter: Cat{age=3}
map:Cat{age=3}
filter: Cat{age=2}
map:Cat{age=2}

可以看到,中間操作的filter和map組合到一起交叉執(zhí)行了,盡管他們是兩個(gè)獨(dú)立的操作(這個(gè)技術(shù)叫作循環(huán)合并

這個(gè)合并主要是由流式操作根據(jù)系統(tǒng)的性能來自行決定的

既然講到了循環(huán)合并,那下面捎帶說下短路技巧

短路這個(gè)詞大家應(yīng)該比較熟悉(比如腦子短路什么的),指的是本來A->B->C是都要執(zhí)行的,但是在B的地方短路了,所以就變成了A->C了

這里的短路指的是中間操作,由于某些原因(比如下面的limit),導(dǎo)致只執(zhí)行了部分,沒有全部去執(zhí)行

我們來修改下上面的例子(加了一個(gè)中間操作limit):

List<Cat> list = Arrays.asList(new Cat(1, "tangyuan"), new Cat(3, "dangdang"), new Cat(2, "milu"));
List<String> listNameNew = list.stream()
.filter(cat -> {
   System.out.println("filter: " + cat);
   return cat.getAge() > 1;
})
.map(cat-> {
   System.out.println("map:" + cat);
   return cat.getName();
})
 // 只加了這一行
.limit(1)
.collect(Collectors.toList());

輸出如下:

filter: Cat{age=1}
filter: Cat{age=3}
map:Cat{age=3}

可以看到,因?yàn)閘imit(1)只需要一個(gè)元素,所以filter過濾時(shí),只要找到一個(gè)滿足條件的,就會(huì)停止過濾操作(后面的元素就放棄了),這個(gè)技巧叫做短路技巧

這個(gè)就很大程度上體現(xiàn)了中間操作的組合順序帶來的優(yōu)點(diǎn):需要多少,處理多少,即按需處理

4. 流的特點(diǎn)

特點(diǎn)有三個(gè):

  • 聲明性:簡(jiǎn)潔,易讀,代碼行數(shù)大大減少(每天有代碼行數(shù)要求的公司除外)

  • 可復(fù)合:更靈活,各種組合都可以(只要你想,只要流有)

  • 可并行:性能更好(不用我們?nèi)懚嗑€程,多好)

5. 流式操作和集合操作的區(qū)別:

現(xiàn)在我們?cè)賮砘仡櫹麻_頭例子中的集合操作:篩選->排序->提取

List<Cat> listTemp = new ArrayList<>();
// 1. 篩選
for(Cat cat: list){
 if(cat.getAge()>1){
   listTemp.add(cat);
}
}
// 2. 排序
listTemp.sort(new Comparator<Cat>() {
 @Override
 public int compare(Cat o1, Cat o2) {
   // 遞增排序
   return Integer.compare(o1.getAge(), o2.getAge());
/**
   * Q:為啥不用減法 return o1.getAge() - o2.getAge()?
   * A:因?yàn)闇p法會(huì)有數(shù)據(jù)溢出的風(fēng)險(xiǎn)
   * 如果o1.getAge()為20億,o2.getAge()為-2億,那么結(jié)果就會(huì)超過int的極限21億多
   **/
}
});
// 3. 提取名字
List<String> listName = new ArrayList<>();
for(Cat cat: listTemp){
 listName.add(cat.getName());
}
System.out.println(listName);

可以看到跟流式操作不一樣的有兩點(diǎn):

  • 集合操作中有一個(gè)listTemp臨時(shí)變量(流式操作沒),

  • 集合操作一直都在處理數(shù)據(jù)(而流式操作是直到最后一步的終端操作才會(huì)去處理數(shù)據(jù)),依次篩選->排序->提取名字,是順序執(zhí)行的

下面我們用表格來列出區(qū)別,應(yīng)該會(huì)直觀點(diǎn)

 流式操作集合操作
功能處理數(shù)據(jù)為主存儲(chǔ)數(shù)據(jù)為主
迭代方式內(nèi)部迭代(只迭代一次),只需聲明,不需要實(shí)現(xiàn),流內(nèi)部自己有實(shí)現(xiàn))外部迭代(可一直迭代)需要自己foreach
處理數(shù)據(jù)直到終端操作,才會(huì)開始真正處理數(shù)據(jù)(按需處理)一直都在處理數(shù)據(jù)(全部處理)

用生活中的例子來對(duì)比的話,可以用電影來比喻

流就好比在線觀看,集合就好本地觀看(下載到本地)

總結(jié)

流是什么:

  • 流是一種以聲明性的方式來處理數(shù)據(jù)的API

  • 流是從支持數(shù)據(jù)處理操作生成的元素序列

    • 源:數(shù)據(jù)的來源,比如集合,文件等(本節(jié)只介紹了集合的流式操作,因?yàn)橛玫谋容^多;后面有空再介紹其他的)

    • 數(shù)據(jù)處理操作:就是流的中間操作,比如filter, map

    • 元素序列:通過流的終端操作,返回的結(jié)果集

  • 流的操作流程:

    • 創(chuàng)建流 -> 中間操作 -> 終端操作

    • 中間操作只是聲明,不真實(shí)處理數(shù)據(jù),直到終端操作開始才會(huì)執(zhí)行

  • 循環(huán)合并:中間操作會(huì)自由組合(流根據(jù)系統(tǒng)自己來決定組合的順序)

  • 短路技巧:如果中間操作處理的數(shù)據(jù)已經(jīng)達(dá)到需求,則會(huì)立即停止處理數(shù)據(jù)(比如limit(1),則當(dāng)處理完1個(gè)就會(huì)停止處理)

  • 流式操作和集合操作的區(qū)別:

    • 按需處理,集合全處理

    • 流主攻數(shù)據(jù)處理,集合主攻數(shù)據(jù)存儲(chǔ)

    • 簡(jiǎn)潔,集合

    • 內(nèi)部迭代(只迭代一次,完后流會(huì)消失),集合外部迭代(可一直迭代)

到此這篇關(guān)于Java8中Stream流式操作的文章就介紹到這了,更多相關(guān)Java8中Stream流式操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java并發(fā)編程示例(五):線程休眠與恢復(fù)

    Java并發(fā)編程示例(五):線程休眠與恢復(fù)

    這篇文章主要介紹了Java并發(fā)編程示例(五):線程休眠與恢復(fù),在本節(jié),我們將開發(fā)一個(gè)程序,使用sleep()方法來實(shí)現(xiàn)每秒鐘打印一次當(dāng)前時(shí)間,需要的朋友可以參考下
    2014-12-12
  • java必學(xué)必會(huì)之線程(2)

    java必學(xué)必會(huì)之線程(2)

    本文對(duì)java線程進(jìn)行深入學(xué)習(xí),重點(diǎn)介紹了線程同步問題、線程死鎖問題,感興趣的小伙伴們可以參考一下
    2015-12-12
  • SpringBoot實(shí)現(xiàn)elasticsearch索引操作的代碼示例

    SpringBoot實(shí)現(xiàn)elasticsearch索引操作的代碼示例

    這篇文章主要給大家介紹了SpringBoot如何實(shí)現(xiàn)elasticsearch 索引操作,文中有詳細(xì)的代碼示例,感興趣的同學(xué)可以參考閱讀下
    2023-07-07
  • 詳解java關(guān)于對(duì)象的比較

    詳解java關(guān)于對(duì)象的比較

    這篇文章主要為大家介紹了java關(guān)于對(duì)象的比較,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 基于spring-security 401 403錯(cuò)誤自定義處理方案

    基于spring-security 401 403錯(cuò)誤自定義處理方案

    這篇文章主要介紹了基于spring-security 401 403錯(cuò)誤自定義處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • idea指定maven的settings文件不生效的問題解決

    idea指定maven的settings文件不生效的問題解決

    本文主要介紹了idea指定maven的settings文件不生效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java實(shí)現(xiàn)五子棋游戲單機(jī)版(1.0)

    Java實(shí)現(xiàn)五子棋游戲單機(jī)版(1.0)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)五子棋游戲單機(jī)版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • SSM項(xiàng)目中配置LOG4J日志的方法

    SSM項(xiàng)目中配置LOG4J日志的方法

    本篇文章主要介紹了SSM項(xiàng)目中配置LOG4J日志的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java多線程并發(fā)FutureTask使用詳解

    Java多線程并發(fā)FutureTask使用詳解

    Java?的多線程機(jī)制本質(zhì)上能夠完成兩件事情,異步計(jì)算和并發(fā),F(xiàn)utureTask?是基于?Runnable?實(shí)現(xiàn)的一個(gè)可取消的異步調(diào)用?API,本文給大家介紹Java?多線程并發(fā)FutureTask及基本使用,需要的朋友可以參考下
    2022-06-06
  • springboot的logging.group日志分組方法源碼流程解析

    springboot的logging.group日志分組方法源碼流程解析

    這篇文章主要為大家介紹了springboot的logging.group日志分組方法源碼流程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評(píng)論