国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

《Java8實(shí)戰(zhàn)》-第五章讀書筆記(使用流Stream-01)

OldPanda / 2692人閱讀

摘要:跳過元素流還支持方法,返回一個(gè)扔掉了前個(gè)元素的流。歸約到目前為止,我們見到過的終端操作都是返回一個(gè)之類的或?qū)ο蟮取_@樣的查詢可以被歸類為歸約操作將流歸約成一個(gè)值。通過反復(fù)使用加法,你把一個(gè)數(shù)字列表歸約成了一個(gè)數(shù)字。

使用流

在上一篇的讀書筆記中,我們已經(jīng)看到了流讓你從外部迭代轉(zhuǎn)向內(nèi)部迭代。這樣,你就用不著寫下面這樣的代碼來顯式地管理數(shù)據(jù)集合的迭代(外部迭代)了:

/**
 * 菜單
 */
public static final List MENU =
        Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 400, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));
List menu = Dish.MENU;
List vegetarianDishes = new ArrayList<>();
for(Dish d: menu){
    if(d.isVegetarian()){
        vegetarianDishes.add(d);
    }
}

我們可以使用支持 filter 和 collect 操作的Stream API(內(nèi)部迭代)管理對(duì)集合數(shù)據(jù)的迭代。
你只需要將篩選行為作為參數(shù)傳遞給 filter 方法就行了。

List vegetarianDishes =
                menu.stream()
                        .filter(Dish::isVegetarian)
                        .collect(toList());

這種處理數(shù)據(jù)的方式很有用,因?yàn)槟阕孲treamAPI管理如何處理數(shù)據(jù)。這樣StreamAPI就可以在背后進(jìn)行多種優(yōu)化。此外,使用內(nèi)部迭代的話,StreamAPI可以決定并行運(yùn)行你的代碼。這要是用外部迭代的話就辦不到了,因?yàn)槟阒荒苡脝我痪€程挨個(gè)迭代。接下來,你將會(huì)看到StreamAPI支持的許多操作。這些操作能讓你快速完成復(fù)雜的數(shù)據(jù)查詢,如篩選、切片、映射、查找、匹配和歸約。

切片和篩選

我們來看看如何選擇流中的元素:用謂詞篩選,篩選出各不相同的元素,忽略流中的頭幾個(gè)元素,或?qū)⒘鹘囟讨林付ㄩL(zhǎng)度。

用謂詞篩選

Streams 接口支持 filter方法(你現(xiàn)在應(yīng)該很熟悉了)。該操作會(huì)接受一個(gè)謂詞(一個(gè)返回boolean 的函數(shù))作為參數(shù),并返回一個(gè)包括所有符合謂詞的元素的流。

List vegetarianDishes =
                menu.stream()
                        // 方法引用檢查菜肴是否適合素食者
                        .filter(Dish::isVegetarian)
                        .collect(toList());

篩選各異的元素

流還支持一個(gè)叫作 distinct 的方法,它會(huì)返回一個(gè)元素各異(根據(jù)流所生成元素的hashCode 和 equals 方法實(shí)現(xiàn))的流。例如,以下代碼會(huì)篩選出列表中所有的偶數(shù),并確保沒有重復(fù)。

List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
                .filter(i -> i % 2 == 0)
                .distinct()
                .forEach(System.out::println);

首先是篩選出偶數(shù),然后檢查是否有重復(fù),最后打印。

截短流

流支持 limit(n) 方法,該方法會(huì)返回一個(gè)不超過給定長(zhǎng)度的流。所需的長(zhǎng)度作為參數(shù)傳遞
給 limit 。如果流是有序的,則最多會(huì)返回前 n 個(gè)元素。比如,你可以建立一個(gè) List ,選出熱量超過300卡路里的頭三道菜:

List dishes = menu.stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .collect(toList());
// pork beef chicken
dishes.forEach(dish -> System.out.println(dish.getName()));

上面的代碼展示了filter和limit的組合。我們可以看到,該方法之篩選出來了符合謂詞的頭三個(gè)元素,然后就立即返回了結(jié)果。請(qǐng)注意limit也可以放在無序流上比如源是一個(gè) Set 。這種情況下, limit 的結(jié)果不會(huì)以任何順序排列。

跳過元素

流還支持 skip(n) 方法,返回一個(gè)扔掉了前n個(gè)元素的流。如果流中元素不足n個(gè),則返回一個(gè)空流。請(qǐng)注意,limit(n)和skip(n)是互補(bǔ)的!例如,下面的代碼將跳過超過300卡路里的頭兩道菜,并返回剩下的。

List dishes = menu.stream()
                .filter(d -> d.getCalories() > 300)
                // 跳過前兩個(gè)
                .skip(2)
                .collect(toList());
// chicken french fries rice pizza prawns salmon
dishes.forEach(dish -> System.out.println(dish.getName()));
映射

一個(gè)非常常見的數(shù)據(jù)處理套路就是從某些對(duì)象中選擇信息。比如在SQL里,你可以從表中選擇一列。Stream API也通過 map 和 flatMap 方法提供了類似的工具。

對(duì)流中每一個(gè)元素應(yīng)用函數(shù)

流支持 map 方法,它會(huì)接受一個(gè)函數(shù)作為參數(shù)。這個(gè)函數(shù)會(huì)被應(yīng)用到每個(gè)元素上,并將其映
射成一個(gè)新的元素(使用映射一詞,是因?yàn)樗娃D(zhuǎn)換類似,但其中的細(xì)微差別在于它是“創(chuàng)建一
個(gè)新版本”而不是去“修改”)。例如,下面的代碼把方法引用 Dish::getName 傳給了 map 方法,來提取流中菜肴的名稱:

List dishNames = menu.stream()
                .map(Dish::getName)
                .collect(toList());
// [pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon]
System.out.println(dishNames);

getName方法返回的是一個(gè)String,所以map方法輸出的流類型就是Stream。當(dāng)然,我們也可以獲取通過map獲取其他的屬性。比如:我需要知道這個(gè)菜單的名字有多長(zhǎng),那么我們可以這樣做:

List len = menu.stream()
                .map(dish -> dish.getName().length())
                .collect(toList());
// [4, 4, 7, 12, 4, 12, 5, 6, 6]
System.out.println(len);

是的,就是這么簡(jiǎn)單,當(dāng)我們只需要獲取某個(gè)對(duì)象中的某個(gè)屬性時(shí),通過map就可以實(shí)現(xiàn)了。

流的扁平化

你已經(jīng)看到如何使用 map方法返回列表中每個(gè)菜單名稱的長(zhǎng)度了。讓我們拓展一下:對(duì)于一張單詞 表 , 如 何 返 回 一 張 列 表 , 列 出 里 面 各 不 相 同 的 字 符 呢 ? 例 如 , 給 定 單 詞 列 表["Hello","World"] ,你想要返回列表 ["H","e","l", "o","W","r","d"] 。

你可能馬上會(huì)想到,將每個(gè)單詞映射成一張字符表,然后調(diào)用distance 來過濾重復(fù)的字符。

List words = Arrays.asList("Hello", "World");
List wordList = words.stream()
        .map(word -> word.split(""))
        .distinct()
        .collect(Collectors.toList());
wordList.forEach(wordArray -> {
    for (String s : wordArray) {
        System.out.print(s);
    }
    System.out.println();
});

執(zhí)行結(jié)果:

Hello
World

執(zhí)行完后一看,不對(duì)呀。仔細(xì)想一想:我們把["Hello", "World"]這兩個(gè)單詞把它們分割稱為了字符數(shù)組,["H", "e", "l", "l", "o"],["W", "o", "r", "l", "d"]。然后將這個(gè)字符數(shù)組去判斷是否重復(fù),不是一個(gè)字符是否重復(fù),而是這一個(gè)字符數(shù)組是否有重復(fù)。所以,打印出來就是Hello World。

幸好可以用flatMap來解決這個(gè)問題!讓我們一步步地來解決它。

嘗試使用 map 和 Arrays.stream()

首先,我們需要一個(gè)字符流,而不是數(shù)組流。有一個(gè)叫作Arrays.stream()的方法可以接受
一個(gè)數(shù)組并產(chǎn)生一個(gè)流,例如:
String[] arrayOfWords = {"Hello", "World"};
Stream streamOfwords = Arrays.stream(arrayOfWords);
按照剛剛上面的做法,使用map和Arrays.stream(),顯然是不行的。
這是因?yàn)椋悻F(xiàn)在得到的是一個(gè)流的列表(更準(zhǔn)確地說是Stream)!的確,
你先是把每個(gè)單詞轉(zhuǎn)換成一個(gè)字母數(shù)組,然后把每個(gè)數(shù)組變成了一個(gè)獨(dú)立的流。

使用 flatMap

我們可以像下面這樣使用flatMap來解決這個(gè)問題:
String[] arrayOfWords = {"Hello", "World"};
Stream streamOfwords = Arrays.stream(arrayOfWords);
List uniqueCharacters = streamOfwords
        // 將每個(gè)單詞轉(zhuǎn)換為由其字母構(gòu)成的數(shù)組
        .map(w -> w.split(""))
        // 將各個(gè)生成流扁平化為單個(gè)流
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());
// HeloWrd
uniqueCharacters.forEach(System.out::print);

太棒了,實(shí)現(xiàn)了我們想要的效果!使用flatMap方法的效果是,各個(gè)數(shù)組并不是分別映射成為一個(gè)流,而是映射成流的內(nèi)容。所有使用map(s -> split(""))時(shí)生成的單個(gè)流都被合并起來,即扁平化為一個(gè)流。一言以蔽之, flatMap 方法讓你把一個(gè)流中的每個(gè)值都換成另一個(gè)流,然后把所有的流連接起來成為一個(gè)流。

查找和匹配

另一個(gè)常見的數(shù)據(jù)處理套路是看看數(shù)據(jù)集中的某些元素是否匹配一個(gè)給定的屬性。Stream
API通過 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny 方法提供了這樣的工具。

檢查謂詞是否至少匹配一個(gè)元素

anyMatch 方法可以回答“流中是否有一個(gè)元素能匹配給定的謂詞”。比如,你可以用它來看
看菜單里面是否有素食可選擇:

if(menu.stream().anyMatch(Dish::isVegetarian)){
    System.out.println("有素菜,不用擔(dān)心!");
}

anyMatch 方法返回一個(gè) boolean ,因此是一個(gè)終端操作。

檢查謂詞是否匹配所有元素

allMatch 方法的工作原理和 anyMatch 類似,但它會(huì)看看流中的元素是否都能匹配給定的謂詞。比如,你可以用它來看看菜品是否有利健康(即所有菜的熱量都低于1000卡路里):

boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);

noneMatch
和 allMatch 相對(duì)的是 noneMatch 。它可以確保流中沒有任何元素與給定的謂詞匹配。比如,
你可以用 noneMatch 重寫前面的例子:

boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);

anyMatch 、 allMatch 和 noneMatch 這三個(gè)操作都用到了我們所謂的短路,這就是大家熟悉
的Java中 && 和 || 運(yùn)算符短路在流中的版本。

查找元素

findAny方法返回當(dāng)前流中的任意元素。它可以與其他流結(jié)合操作使用。比如,你可能想找到一道素食菜肴。我們可以使用filter和findAny來實(shí)現(xiàn):

Optional dish = menu.stream()
                .filter(Dish::isVegetarian)
                .findAny();

OK,這樣就完成我們想要的了。但是,你會(huì)發(fā)現(xiàn)它返回的是一個(gè)Optional。Optional類(java.util.Optional)是一個(gè)容器類,代表一個(gè)值存在或者不存在。在上面的代碼中,findAny可能什么都沒找到。。Java 8的庫設(shè)計(jì)人員引入了 Optional ,這
樣就不用返回眾所周知容易出問題的 null 了。很好的解決了“十億美元的錯(cuò)誤”!不過我們現(xiàn)在不討論它,以后再去詳細(xì)的了解它是如何的使用。

查找第一個(gè)元素

有些流有一個(gè)出現(xiàn)順序(encounter order)來指定流中項(xiàng)目出現(xiàn)的邏輯順序(比如由 List 或
排序好的數(shù)據(jù)列生成的流)。對(duì)于這種流,你可能想要找到第一個(gè)元素。為此有一個(gè) findFirst
方法,它的工作方式類似于 findany 。例如,給定一個(gè)數(shù)字列表,下面的代碼能找出第一個(gè)平方
能被3整除的數(shù):

List someNumbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Optional firstSquareDivisibleByThree =
        someNumbers.stream()
                .map(x -> x * x)
                .filter(x -> x % 3 == 0)
                // 9
                .findFirst();

是的,通過鏈?zhǔn)秸{(diào)用,就完成了我們想要的功能,比起以前來說好太多了。你可能有一個(gè)疑問,findAny和findFrist在什么時(shí)候使用比較好或者說兩個(gè)都存在怎么辦。findAny和findFrist是并行的。找到第一個(gè)元素在并行上限制的更多。如果,你不關(guān)心放回元素是哪一個(gè),請(qǐng)使用findAny,因?yàn)樗谑褂貌⑿辛鲿r(shí)限制比較少。

歸約

到目前為止,我們見到過的終端操作都是返回一個(gè) boolean ( allMatch 之類的)、 void
( forEach )或 Optional 對(duì)象( findAny 等)。你也見過了使用 collect 來將流中的所有元素組合成一個(gè) List 。接下來,我們將會(huì)看到如何把一個(gè)流中的元素組合起來,使用reduce操作來表達(dá)更復(fù)雜的查詢,比如“計(jì)算菜單中的總卡路里”或者“菜單中卡路里最高的菜是哪一個(gè)”。此類查詢需要將流中的所有元素反復(fù)結(jié)合起來,得到一個(gè)值,比如一個(gè)Integer。這樣的查詢可以被歸類為歸約操作(將流歸約成一個(gè)值)。用函數(shù)式編程語言的術(shù)語來說,這稱為折疊(fold),因?yàn)槟憧梢詫⑦@個(gè)操作看成把一張長(zhǎng)長(zhǎng)的紙(你的流)反復(fù)折疊成一個(gè)小方塊,而這就是折疊操作的結(jié)果。

元素求和

在沒有reduce之前,我們先用foreach循環(huán)來對(duì)數(shù)字列表中的元素求和:

int sum = 0;
for (int x : numbers) {
    sum += x;
}

numbers 中的每個(gè)元素都用加法運(yùn)算符反復(fù)迭代來得到結(jié)果。通過反復(fù)使用加法,你把一個(gè)
數(shù)字列表歸約成了一個(gè)數(shù)字。

要是還能把所有的數(shù)字相乘,而不必去復(fù)制粘貼這段代碼,豈不是很好?這正是 reduce 操
作的用武之地,它對(duì)這種重復(fù)應(yīng)用的模式做了抽象。你可以像下面這樣對(duì)流中所有的元素求和:

List numbers = Arrays.asList(3, 4, 5, 1, 2);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 15
System.out.println(sum);

我們很簡(jiǎn)單的就完成了元素與元素相加最后得到的結(jié)果。如果是元素與元素相乘,也很簡(jiǎn)單:

numbers.stream().reduce(1, (a, b) -> a * b);

是的,就是這么簡(jiǎn)單!我們還可以使用方法引用來簡(jiǎn)化求和的代碼,讓它看起來更加簡(jiǎn)潔:

int sum2 = numbers.stream().reduce(0, Integer::sum);

無初始值
reduce 還有一個(gè)重載的變體,它不接受初始值,但是會(huì)返回一個(gè) Optional 對(duì)象:

Optional sum = numbers.stream().reduce((a, b) -> (a + b));

為什么它返回一個(gè) Optional 呢?考慮流中沒有任何元素的情況。 reduce 操作無
法返回其和,因?yàn)樗鼪]有初始值。這就是為什么結(jié)果被包裹在一個(gè) Optional 對(duì)象里,以表明和
可能不存在。現(xiàn)在看看用 reduce 還能做什么。

最大值和最小值

原來,只要用歸約就可以計(jì)算最大值和最小值了!讓我們來看看如何利用剛剛學(xué)到的 reduce
來計(jì)算流中最大或最小的元素。

Optional max = numbers.stream().reduce(Integer::max);

reduce 操作會(huì)考慮新值和流中下一個(gè)元素,并產(chǎn)生一個(gè)新的最大值,直到整個(gè)流消耗完!就像這樣:

3 - 4 - 5 - 1 - 2
↓
3 → 4
    ↓
    4 → 5
        ↓
        5 → 1
            ↓
            5 → 2
                ↓
                5

通過這樣的形式去比較哪個(gè)數(shù)值是最大的!如果,你獲取最小的數(shù)值,也很簡(jiǎn)單只需要這樣:

Optional min = numbers.stream().reduce(Integer::min);

好了,關(guān)于流的使用就想講到這了,在下一節(jié)中我們將會(huì)付諸實(shí)戰(zhàn),而不是看完了之后不去使用它,相信過不了多久我們就會(huì)忘記的!

小結(jié)

這一章的讀書筆記中,我們學(xué)習(xí)和了解到了:

Streams API可以表達(dá)復(fù)雜的數(shù)據(jù)處理查詢。

你可以使用 filter 、 distinct 、 skip 和 limit 對(duì)流做篩選和切片。

你可以使用 map 和 flatMap 提取或轉(zhuǎn)換流中的元素。

你可以使用 findFirst 和 findAny 方法查找流中的元素。你可以用 allMatch、noneMatch 和 anyMatch 方法讓流匹配給定的謂詞。

這些方法都利用了短路:找到結(jié)果就立即停止計(jì)算;沒有必要處理整個(gè)流。

你可以利用 reduce 方法將流中所有的元素迭代合并成一個(gè)結(jié)果,例如求和或查找最大

元素。

代碼

Github: chap5

Gitee: chap5

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76968.html

相關(guān)文章

  • Java8實(shí)戰(zhàn)》-五章讀書筆記使用Stream-02)

    摘要:第三個(gè)問題查找所有來自于劍橋的交易員,并按姓名排序。第六個(gè)問題打印生活在劍橋的交易員的所有交易額。第八個(gè)問題找到交易額最小的交易。 付諸實(shí)戰(zhàn) 在本節(jié)中,我們會(huì)將迄今學(xué)到的關(guān)于流的知識(shí)付諸實(shí)踐。我們來看一個(gè)不同的領(lǐng)域:執(zhí)行交易的交易員。你的經(jīng)理讓你為八個(gè)查詢找到答案。 找出2011年發(fā)生的所有交易,并按交易額排序(從低到高)。 交易員都在哪些不同的城市工作過? 查找所有來自于劍橋的交易...

    liangzai_cool 評(píng)論0 收藏0
  • 《java 8 實(shí)戰(zhàn)讀書筆記 -五章 使用

    摘要:比如,你可以建立一個(gè),選出熱量超過卡路里的頭三道菜請(qǐng)注意也可以用在無序流上,比如源是一個(gè)。跳過元素流還支持方法,返回一個(gè)扔掉了前個(gè)元素的流。一般來說,應(yīng)該使用來對(duì)這種流加以限制,以避免打印無窮多個(gè)值。 一、篩選和切片 1.用謂詞篩選 Streams接口支持filter方法。該操作會(huì)接受一個(gè)謂詞(一個(gè)返回boolean的函數(shù))作為參數(shù),并返回一個(gè)包括所有符合謂詞的元素的流。例如篩選出所有...

    Richard_Gao 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-讀書筆記第一章(02)

    摘要:實(shí)戰(zhàn)讀書筆記第一章從方法傳遞到接著上次的,繼續(xù)來了解一下,如果繼續(xù)簡(jiǎn)化代碼。去掉并且生成的數(shù)字是萬,所消耗的時(shí)間循序流并行流至于為什么有時(shí)候并行流效率比循序流還低,這個(gè)以后的文章會(huì)解釋。 《Java8實(shí)戰(zhàn)》-讀書筆記第一章(02) 從方法傳遞到Lambda 接著上次的Predicate,繼續(xù)來了解一下,如果繼續(xù)簡(jiǎn)化代碼。 把方法作為值來傳遞雖然很有用,但是要是有很多類似與isHeavy...

    lushan 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-第四章讀書筆記(引入Stream

    摘要:內(nèi)部迭代與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的。流只能遍歷一次請(qǐng)注意,和迭代器類似,流只能遍歷一次。 流(Stream) 流是什么 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級(jí)迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼了!我會(huì)在后面的筆記中...

    _ivan 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-第六章讀書筆記(用收集數(shù)據(jù)-02)

    摘要:使用流收集數(shù)據(jù)分區(qū)分區(qū)是分組的特殊情況由一個(gè)謂詞返回一個(gè)布爾值的函數(shù)作為分類函數(shù),它稱分區(qū)函數(shù)。這種情況下,累加器對(duì)象將會(huì)直接用作歸約過程的最終結(jié)果。這也意味著,將累加器不加檢查地轉(zhuǎn)換為結(jié)果是安全的。 使用流收集數(shù)據(jù) 分區(qū) 分區(qū)是分組的特殊情況:由一個(gè)謂詞(返回一個(gè)布爾值的函數(shù))作為分類函數(shù),它稱分區(qū)函數(shù)。分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組 Map 的鍵類型是 Boolean ...

    jcc 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<