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