摘要:比如,你可以建立一個,選出熱量超過卡路里的頭三道菜請注意也可以用在無序流上,比如源是一個。跳過元素流還支持方法,返回一個扔掉了前個元素的流。一般來說,應(yīng)該使用來對這種流加以限制,以避免打印無窮多個值。
一、篩選和切片 1.用謂詞篩選
Streams接口支持filter方法。該操作會接受一個謂詞(一個返回
boolean的函數(shù))作為參數(shù),并返回一個包括所有符合謂詞的元素的流。例如篩選出所有素菜,創(chuàng)建一張素食菜單:
List2.篩選各異的元素vegetarianMenu = menu.stream() .filter(Dish::isVegetarian) .collect(toList());
流還支持一個叫作distinct的方法,它會返回一個元素各異(根據(jù)流所生成元素的
hashCode和equals方法實現(xiàn))的流。例如,以下代碼會篩選出列表中所有的偶數(shù),并確保沒有
重復(fù)。
Listnumbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream() .filter(i -> i % 2 == 0) .distinct() .forEach(System.out::println);
hashcode( )和equals( )3.截短流
1.Java中的hashCode()的作用
hashCode()的作用是為了提高在散列結(jié)構(gòu)存儲中查找的效率,在線性表中沒有作用;只有每個對象的 hash 碼盡可能不同才能保證散列的存取性能,事實上 Object 類提供的默認(rèn)實現(xiàn)確實保證每個對象的 hash 碼不同(在對象的內(nèi)存地址基礎(chǔ)上經(jīng)過特定算法返回一個 hash 碼)。在 Java 有些集合類(HashSet)中要想保證元素不重復(fù)可以在每增加一個元素就通過對象的 equals 方法比較一次,那么當(dāng)元素很多時后添加到集合中的元素比較的次數(shù)就非常多了,也就是說如果集合中現(xiàn)在已經(jīng)有 3000 個元素則第 3001 個元素加入集合時就要調(diào)用 3000 次 equals 方法,這顯然會大大降低效率,于是 Java 采用了哈希表的原理,這樣當(dāng)集合要添加新的元素時會先調(diào)用這個元素的 hashCode 方法就一下子能定位到它應(yīng)該放置的物理位置上(實際可能并不是),如果這個位置上沒有元素則它就可以直接存儲在這個位置上而不用再進(jìn)行任何比較了,如果這個位置上已經(jīng)有元素了則就調(diào)用它的 equals 方法與新元素進(jìn)行比較,相同的話就不存,不相同就散列其它的地址,這樣一來實際調(diào)用 equals 方法的次數(shù)就大大降低了,幾乎只需要一兩次,而 hashCode 的值對于每個對象實例來說是一個固定值。
2.Java中重寫equals()方法時盡量要重寫hashCode()方法的原因
當(dāng) equals 方法被重寫時通常有必要重寫 hashCode 方法來維護(hù) hashCode 方法的常規(guī)協(xié)定,該協(xié)定聲明相等對象必須具有相等的哈希碼,如果不這樣做的話就會違反 hashCode 方法的常規(guī)約定,從而導(dǎo)致該類無法結(jié)合所有基于散列的集合一起正常運作,這樣的集合包括 HashMap、HashSet、Hashtable 等引申:HashMap實現(xiàn)原理及源碼分析
注意:哈希沖突的解決方案有多種:開放定址法(發(fā)生沖突,繼續(xù)尋找下一塊未被占用的存儲地址),再散列函數(shù)法,鏈地址法,而HashMap即是采用了鏈地址法,也就是數(shù)組+鏈表的方式
流支持limit(n)方法,該方法會返回一個不超過給定長度的流。所需的長度作為參數(shù)傳遞給limit。如果流是有序的,則最多會返回前n個元素。比如,你可以建立一個List,選出熱量超過300卡路里的頭三道菜:
Listdishes = menu.stream() .filter(d -> d.getCalories() > 300) .limit(3) .collect(toList());
請注意limit也可以用在無序流上,比如源是一個Set。這種情況下,limit的結(jié)果不會以任何順序排列。
4.跳過元素流還支持skip(n)方法,返回一個扔掉了前n個元素的流。如果流中元素不足n個,則返回一個空流。
List二、映射 1.對流中每一個元素應(yīng)用函數(shù)dishes = menu.stream() .filter(d -> d.getCalories() > 300) .skip(2) .collect(toList());
提取流中菜肴的名稱:
List2.流的扁平化dishNames = menu.stream() .map(Dish::getName) .collect(toList());
ListuniqueCharacters = words.stream() .map(w -> w.split("")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList());
使用flatMap方法的效果是,各個數(shù)組并不是分別映射成一個流,而是映射成流的內(nèi)容。所有使用map(Arrays::stream)時生成的單個流都被合并起來,即扁平化為一個流。一言以蔽之,flatmap方法讓你把一個流中的每個值都換成另一個流,然后把所有的流連接起來成為一個流。
三、查找和匹配 1.檢查謂詞是否至少匹配一個元素anyMatch方法可以回答“流中是否有一個元素能匹配給定的謂詞”。比如,你可以用它來看看菜單里面是否有素食可選擇:
if(menu.stream().anyMatch(Dish::isVegetarian)){ System.out.println("The menu is (somewhat) vegetarian friendly!!"); }
anyMatch方法返回一個boolean,是一個終端操作2.檢查謂詞是否匹配所有元素
allMatch方法的工作原理和anyMatch類似,但它會看看流中的元素是否都能匹配給定的謂詞
boolean isHealthy = menu.stream() .allMatch(d -> d.getCalories() < 1000);
noneMatch和allMatch相對的是noneMatch。它可以確保流中沒有任何元素與給定的謂詞匹配。比如,
你可以用noneMatch重寫前面的例子:
boolean isHealthy = menu.stream() .noneMatch(d -> d.getCalories() >= 1000);
anyMatch、allMatch和noneMatch這三個操作都用到了我們所謂的短路3.查找元素
findAny方法將返回當(dāng)前流中的任意元素。它可以與其他流操作結(jié)合使用。比如,你可能想
找到一道素食菜肴。你可以結(jié)合使用filter和findAny方法來實現(xiàn)這個查詢:
Optionaldish = menu.stream() .filter(Dish::isVegetarian) .findAny();
4.查找第一個元素Optional簡介
Optional類(java.util.Optional)是一個容器類,代表一個值存在或不存在 。在上面的代碼中,findAny可能什么元素都沒找到。Java 8的庫設(shè)計人員引入了Optional,這樣就不用返回眾所周知容易出問題的null了。
Optional里面幾種可以迫使你顯式地檢查值是否存在或處理值不存在的情形的方法也不錯。isPresent()將在Optional包含值的時候返回true, 否則返回false。
ifPresent(Consumer
block)會在值存在的時候執(zhí)行給定的代碼塊。 T get()會在值存在時返回值,否則拋出一個NoSuchElement異常。
T orElse(T other)會在值存在時返回值,否則返回一個默認(rèn)值。
例如,在前面的代碼中你需要顯式地檢查Optional對象中是否存在一道菜可以訪問其名稱:
menu.stream() .filter(Dish::isVegetarian) .findAny() .ifPresent(d -> System.out.println(d.getName());
使用findFirst()方法
List四、歸約 1.元素求和(多歸一)someNumbers = Arrays.asList(1, 2, 3, 4, 5); Optional firstSquareDivisibleByThree = someNumbers.stream() .map(x -> x * x) .filter(x -> x % 3 == 0) .findFirst(); // 9
有初始值
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
reduce接受兩個參數(shù):
(1) 一個初始值,這里是0;
(2) 一個BinaryOperator
無初始值
reduce還有一個重載的變體,它不接受初始值,但是會返回一個Optional對象:
Optionalsum = numbers.stream().reduce((a, b) -> (a + b));
為什么它返回一個Optional2.最大值和最小值呢?
考慮流中沒有任何元素的情況。reduce操作無返回其和,因為它沒有初始值。這就是為什么結(jié)果被包裹在一個Optional對象里,以表明和可能不存在。
eg:
Optionalmax = numbers.stream().reduce(Integer::max);//最大值 Optional min = numbers.stream().reduce(Integer::min);//最小值
map和reduce的連接通常稱為map-reduce模式,因Google用它來進(jìn)行網(wǎng)絡(luò)搜索而出名,因為它很容易并行化。
諸如map或filter等操作會從輸入流中獲取每一個元素,并在輸出流中得到0或1個結(jié)果,這些操作一般都是無狀態(tài)的;但諸如reduce、sum、max等操作需要內(nèi)部狀態(tài)來累積結(jié)果,我們把這些操作叫作有狀態(tài)操作。五、數(shù)值流 1.原始類型流特化 1.1 映射到數(shù)值流
Java 8引入了三個原始類型特化流接口來解決這個問題:IntStream、DoubleStream和LongStream,分別將流中的元素特化為int、long和double,從而避免了暗含的裝箱成本。個接口都帶來了進(jìn)行常用數(shù)值歸約的新方法,比如對數(shù)值流求和的sum,找到最大元素的max,以及min、average等。
可以像下面這樣用mapToInt對menu中的卡路里求和:
int calories = menu.stream() .mapToInt(Dish::getCalories) .sum();
請注意,如果流是空的,sum默認(rèn)返回0。1.2 轉(zhuǎn)換回對象流
要把原始流轉(zhuǎn)換成一般流(每個int都會裝箱成一個Integer),可以使用boxed方法,如下所示:
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); Stream1.3 默認(rèn)值OptionalIntstream = intStream.boxed();
求和的那個例子很容易,因為它有一個默認(rèn)值:0。但是,如果你要計算IntStream中的最大元素,就得換個法子了,因為0是錯誤的結(jié)果。對于三種原始流特化,分別有一個Optional原始類型特化版本:OptionalInt、OptionalDouble和OptionalLong。
例如,要找到IntStream中的最大元素,可以調(diào)用max方法,它會返回一個OptionalInt:
OptionalInt maxCalories = menu.stream() .mapToInt(Dish::getCalories) .max();
現(xiàn)在,如果沒有最大值的話,你就可以顯式處理OptionalInt去定義一個默認(rèn)值了:
int max = maxCalories.orElse(1);2.數(shù)值范圍
假設(shè)你想要生成1和100之間的所有數(shù)字。Java 8引入了兩個可以用于IntStream和LongStream的靜態(tài)方法,幫助生成這種范圍:
range和rangeClosed。這兩個方法都是第一個參數(shù)接受起始值,第二個參數(shù)接受結(jié)束值。但
range是不包含結(jié)束值的,而rangeClosed則包含結(jié)束值。
IntStream evenNumbers = IntStream.rangeClosed(1, 100) .filter(n -> n % 2 == 0); System.out.println(evenNumbers.count());六、構(gòu)建流 1.由值創(chuàng)建流
你可以使用靜態(tài)方法Stream.of,通過顯式值創(chuàng)建一個流。它可以接受任意數(shù)量的參數(shù)。例如,以下代碼直接使用Stream.of創(chuàng)建了一個字符串流。然后,你可以將字符串轉(zhuǎn)換為大寫,再一個個打印出來:
Streamstream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action"); stream.map(String::toUpperCase).forEach(System.out::println);
你可以使用empty得到一個空流,如下所示:
Stream2.由數(shù)組創(chuàng)建流emptyStream = Stream.empty();
int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrays.stream(numbers).sum();3.由文件生成流
Files.lines,它會返回一個由指定文件中的各行構(gòu)成的字符串流。
用這個方法看看一個文件中有多少各不相同的詞:
long uniqueWords = 0; try(Stream4.由函數(shù)生成流:創(chuàng)建無限流lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) .distinct() .count(); } catch(IOException e){ //如果打開文件時出現(xiàn)異常則加以處理 }
Stream API提供了兩個靜態(tài)方法來從函數(shù)生成流:Stream.iterate和Stream.generate。一般來說,應(yīng)該使用limit(n)來對這種流加以限制,以避免打印無窮多個值。
4.1 迭代Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println);
iterate方法接受一個初始值(在這里是0),還有一個依次應(yīng)用在每個產(chǎn)生的新值上的Lambda(UnaryOperator4.2 生成類型)。這里,我們使用Lambda n -> n + 2,返回的是前一個元素加上2。
與iterate方法類似,generate方法也可讓你按需生成一個無限流。但generate不是依次對每個新生成的值應(yīng)用函數(shù)的。它接受一Supplier
看一個簡單的用法:
Stream.generate(Math::random) .limit(5) .forEach(System.out::println);
這段代碼將生成一個流,其中有五個0到1之間的隨機雙精度數(shù)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/74193.html
摘要:正確使用并行流錯用并行流而產(chǎn)生錯誤的首要原因,就是使用的算法改變了某些共享狀態(tài)。高效使用并行流留意裝箱有些操作本身在并行流上的性能就比順序流差還要考慮流的操作流水線的總計算成本。 一、并行流 1.將順序流轉(zhuǎn)換為并行流 對順序流調(diào)用parallel方法: public static long parallelSum(long n) { return Stream.iterate(1L...
摘要:第四章引入流一什么是流流是的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合通過查詢語句來表達(dá),而不是臨時編寫一個實現(xiàn)。 第四章 引入流 一、什么是流 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時編寫一個實現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼。 下面兩段代碼都是用來返回低...
摘要:實戰(zhàn)讀書筆記第一章從方法傳遞到接著上次的,繼續(xù)來了解一下,如果繼續(xù)簡化代碼。去掉并且生成的數(shù)字是萬,所消耗的時間循序流并行流至于為什么有時候并行流效率比循序流還低,這個以后的文章會解釋。 《Java8實戰(zhàn)》-讀書筆記第一章(02) 從方法傳遞到Lambda 接著上次的Predicate,繼續(xù)來了解一下,如果繼續(xù)簡化代碼。 把方法作為值來傳遞雖然很有用,但是要是有很多類似與isHeavy...
摘要:分區(qū)函數(shù)返回一個布爾值,這意味著得到的分組的鍵類型是,于是它最多可以分為兩組是一組,是一組。當(dāng)遍歷到流中第個元素時,這個函數(shù)執(zhí)行時會有兩個參數(shù)保存歸約結(jié)果的累加器已收集了流中的前個項目,還有第個元素本身。 一、收集器簡介 把列表中的交易按貨幣分組: Map transactionsByCurrencies = transactions.stream().collect(groupi...
摘要:內(nèi)部迭代與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的。流只能遍歷一次請注意,和迭代器類似,流只能遍歷一次。 流(Stream) 流是什么 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時編寫一個實現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼了!我會在后面的筆記中...
摘要:收集器用作高級歸約剛剛的結(jié)論又引出了優(yōu)秀的函數(shù)式設(shè)計的另一個好處更易復(fù)合和重用。更具體地說,對流調(diào)用方法將對流中的元素觸發(fā)一個歸約操作由來參數(shù)化。另一個常見的返回單個值的歸約操作是對流中對象的一個數(shù)值字段求和。 用流收集數(shù)據(jù) 我們在前一章中學(xué)到,流可以用類似于數(shù)據(jù)庫的操作幫助你處理集合。你可以把Java 8的流看作花哨又懶惰的數(shù)據(jù)集迭代器。它們支持兩種類型的操作:中間操作(如 filt...
閱讀 2986·2021-11-23 09:51
閱讀 2798·2021-11-11 16:55
閱讀 2907·2021-10-14 09:43
閱讀 1394·2021-09-23 11:22
閱讀 1035·2019-08-30 11:04
閱讀 1663·2019-08-29 11:10
閱讀 956·2019-08-27 10:56
閱讀 3102·2019-08-26 12:01