摘要:第三個(gè)問題查找所有來自于劍橋的交易員,并按姓名排序。第六個(gè)問題打印生活在劍橋的交易員的所有交易額。第八個(gè)問題找到交易額最小的交易。
付諸實(shí)戰(zhàn)
在本節(jié)中,我們會(huì)將迄今學(xué)到的關(guān)于流的知識(shí)付諸實(shí)踐。我們來看一個(gè)不同的領(lǐng)域:執(zhí)行交易的交易員。你的經(jīng)理讓你為八個(gè)查詢找到答案。
找出2011年發(fā)生的所有交易,并按交易額排序(從低到高)。
交易員都在哪些不同的城市工作過?
查找所有來自于劍橋的交易員,并按姓名排序。
返回所有交易員的姓名字符串,按字母順序排序。
有沒有交易員是在米蘭工作的?
打印生活在劍橋的交易員的所有交易額。
所有交易中,最高的交易額是多少?
找到交易額最小的交易。
領(lǐng)域:交易員和交易以下是我們要處理的領(lǐng)域,一個(gè) Traders 和 Transactions 的列表:
Trader raoul = new Trader("Raoul", "Cambridge"); Trader mario = new Trader("Mario", "Milan"); Trader alan = new Trader("Alan", "Cambridge"); Trader brian = new Trader("Brian", "Cambridge"); Listtransactions = Arrays.asList( new Transaction(brian, 2011, 300), new Transaction(raoul, 2012, 1000), new Transaction(raoul, 2011, 400), new Transaction(mario, 2012, 710), new Transaction(mario, 2012, 700), new Transaction(alan, 2012, 950) );
Trader和Transaction類的定義:
public class Trader { private String name; private String city; public Trader(String n, String c){ this.name = n; this.city = c; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Trader{" + "name="" + name + """ + ", city="" + city + """ + "}"; } }
Transaction類:
public class Transaction { private Trader trader; private Integer year; private Integer value; public Transaction(Trader trader, Integer year, Integer value) { this.trader = trader; this.year = year; this.value = value; } public Trader getTrader() { return trader; } public void setTrader(Trader trader) { this.trader = trader; } public Integer getYear() { return year; } public void setYear(Integer year) { this.year = year; } public Integer getValue() { return value; } public void setValue(Integer value) { this.value = value; } @Override public String toString() { return "Transaction{" + "trader=" + trader + ", year=" + year + ", value=" + value + "}"; } }
Listtr2011 = transactions.stream() // 篩選出2011年發(fā)生的所有交易 .filter(transaction -> transaction.getYear() == 2011) // 按照交易額從低到高排序 .sorted(Comparator.comparing(Transaction::getValue)) // 轉(zhuǎn)為集合 .collect(Collectors.toList());
太棒了,第一個(gè)問題我們很輕松的就解決了!首先,將transactions集合轉(zhuǎn)為流,然后給filter傳遞一個(gè)謂詞來選擇2011年的交易,接著按照交易額從低到高進(jìn)行排序,最后將Stream中的所有元素收集到一個(gè)List集合中。
Listcities = transactions.stream() // 提取出交易員所工作的城市 .map(transaction -> transaction.getTrader().getCity()) // 去除已有的城市 .distinct() // 將Stream中所有的元素轉(zhuǎn)為一個(gè)List集合 .collect(Collectors.toList());
是的,我們很簡單的完成了第二個(gè)問題。首先,將transactions集合轉(zhuǎn)為流,然后使用map提取出與交易員相關(guān)的每位交易員所在的城市,接著使用distinct去除重復(fù)的城市(當(dāng)然,我們也可以去掉distinct,在最后我們就要使用collect,將Stream中的元素轉(zhuǎn)為一個(gè)Set集合。collect(Collectors.toSet())),我們只需要不同的城市,最后將Stream中的所有元素收集到一個(gè)List中。
Listtraders = transactions.stream() // 從交易中提取所有的交易員 .map(Transaction::getTrader) // 進(jìn)選擇位于劍橋的交易員 .filter(trader -> "Cambridge".equals(trader.getCity())) // 確保沒有重復(fù) .distinct() // 對(duì)生成的交易員流按照姓名進(jìn)行排序 .sorted(Comparator.comparing(Trader::getName)) .collect(Collectors.toList());
第三個(gè)問題,從交易中提取所有的交易員,然后進(jìn)選擇位于劍橋的交易員確保沒有重復(fù),接著對(duì)生成的交易員流按照姓名進(jìn)行排序。
String traderStr = transactions.stream() // 提取所有交易員姓名,生成一個(gè) Strings 構(gòu)成的 Stream .map(transaction -> transaction.getTrader().getName()) // 只選擇不相同的姓名 .distinct() // 對(duì)姓名按字母順序排序 .sorted() // 逐個(gè)拼接每個(gè)名字,得到一個(gè)將所有名字連接起來的 String .reduce("", (n1, n2) -> n1 + " " + n2);
這些問題,我們都很輕松的就完成!首先,提取所有交易員姓名,生成一個(gè) Strings 構(gòu)成的 Stream并且只選擇不相同的姓名,然后對(duì)姓名按字母順序排序,最后使用reduce將名字拼接起來!
請(qǐng)注意,此解決方案效率不高(所有字符串都被反復(fù)連接,每次迭代的時(shí)候都要建立一個(gè)新
的 String 對(duì)象)。下一章中,你將看到一個(gè)更為高效的解決方案,它像下面這樣使用 joining (其
內(nèi)部會(huì)用到 StringBuilder ):
String traderStr = transactions.stream() .map(transaction -> transaction.getTrader().getName()) .distinct() .sorted() .collect(joining());
boolean milanBased = transactions.stream() // 把一個(gè)謂詞傳遞給 anyMatch ,檢查是否有交易員在米蘭工作 .anyMatch(transaction -> "Milan".equals(transaction.getTrader() .getCity()));
第五個(gè)問題,依舊很簡單把一個(gè)謂詞傳遞給 anyMatch ,檢查是否有交易員在米蘭工作。
transactions.stream() // 選擇住在劍橋的交易員所進(jìn)行的交易 .filter(t -> "Cambridge".equals(t.getTrader().getCity())) // 提取這些交易的交易額 .map(Transaction::getValue) // 打印每個(gè)值 .forEach(System.out::println);
第六個(gè)問題,首先選擇住在劍橋的交易員所進(jìn)行的交易,接著提取這些交易的交易額,然后就打印出每個(gè)值。
OptionalhighestValue = transactions.stream() // 提取每項(xiàng)交易的交易額 .map(Transaction::getValue) // 計(jì)算生成的流中的最大值 .reduce(Integer::max);
第七個(gè)問題,首先提取每項(xiàng)交易的交易額,然后使用reduce計(jì)算生成的流中的最大值。
OptionalsmallestTransaction = transactions.stream() // 通過反復(fù)比較每個(gè)交易的交易額,找出最小的交易 .reduce((t1, t2) -> t1.getValue() < t2.getValue() ? t1 : t2);
是的,第八個(gè)問題很簡單,但是還有更好的做法!流支持 min 和 max 方法,它們可以接受一個(gè) Comparator 作為參數(shù),指定
計(jì)算最小或最大值時(shí)要比較哪個(gè)鍵值:
OptionalsmallestTransaction = transactions.stream() .min(comparing(Transaction::getValue));
上面的八個(gè)問題,我們通過Stream很輕松的就完成了,真是太棒了!
數(shù)值流我們?cè)谇懊婵吹搅丝梢允褂?reduce 方法計(jì)算流中元素的總和。例如,你可以像下面這樣計(jì)
算菜單的熱量:
int calories = menu.stream() .map(Dish::getCalories) .reduce(0, Integer::sum);
這段代碼的問題是,它有一個(gè)暗含的裝箱成本。每個(gè) Integer 都必須拆箱成一個(gè)原始類型,
再進(jìn)行求和。要是可以直接像下面這樣調(diào)用 sum 方法,豈不是更好?
int calories = menu.stream() .map(Dish::getCalories) .sum();
但這是不可能的。問題在于 map 方法會(huì)生成一個(gè) Stream
型,但 Streams 接口沒有定義 sum 方法。為什么沒有呢?比方說,你只有一個(gè)像 menu 那樣的Stream
Java 8引入了三個(gè)原始類型特化流接口來解決這個(gè)問題: IntStream 、 DoubleStream 和
LongStream ,分別將流中的元素特化為 int 、 long 和 double ,從而避免了暗含的裝箱成本。每個(gè)接口都帶來了進(jìn)行常用數(shù)值歸約的新方法,比如對(duì)數(shù)值流求和的 sum ,找到最大元素的max。此外還有在必要時(shí)再把它們轉(zhuǎn)換回對(duì)象流的方法。要記住的是,這些特化的原因并不在于流的復(fù)雜性,而是裝箱造成的復(fù)雜性——即類似 int 和 Integer 之間的效率差異。
1.映射到數(shù)值流
將流轉(zhuǎn)換為特化版本的常用方法是 mapToInt 、 mapToDouble 和 mapToLong 。這些方法和前
面說的 map 方法的工作方式一樣,只是它們返回的是一個(gè)特化流,而不是 Stream
int calories = menu.stream() // 返回一個(gè)IntStream .mapToInt(Dish::getCalories) .sum();
這里, mapToInt 會(huì)從每道菜中提取熱量(用一個(gè) Integer 表示),并返回一個(gè) IntStream
(而不是一個(gè) Stream
路里求和了!請(qǐng)注意,如果流是空的, sum 默認(rèn)返回 0 。 IntStream 還支持其他的方便方法,如
max 、 min 、 average 等。
2.轉(zhuǎn)換回對(duì)象流
同樣,一旦有了數(shù)值流,你可能會(huì)想把它轉(zhuǎn)換回非特化流。例如, IntStream 上的操作只能
產(chǎn)生原始整數(shù): IntStream 的 map 操作接受的Lambda必須接受 int 并返回 int (一個(gè)
IntUnaryOperator )。但是你可能想要生成另一類值,比如 Dish 。為此,你需要訪問 Stream
接口中定義的那些更廣義的操作。要把原始流轉(zhuǎn)換成一般流(每個(gè) int 都會(huì)裝箱成一個(gè)
Integer ),可以使用 boxed 方法,如下所示:
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); Streamstream = intStream.boxed();
3.默認(rèn)值 OptionalInt
求和的那個(gè)例子很容易,因?yàn)樗幸粋€(gè)默認(rèn)值: 0 。但是,如果你要計(jì)算 IntStream 中的最
大元素,就得換個(gè)法子了,因?yàn)?0 是錯(cuò)誤的結(jié)果。如何區(qū)分沒有元素的流和最大值真的是 0 的流呢?
前面我們介紹了 Optional 類,這是一個(gè)可以表示值存在或不存在的容器。 Optional 可以用
Integer 、 String 等參考類型來參數(shù)化。對(duì)于三種原始流特化,也分別有一個(gè) Optional 原始類
型特化版本: OptionalInt 、 OptionalDouble 和 OptionalLong 。
例如,要找到 IntStream 中的最大元素,可以調(diào)用 max 方法,它會(huì)返回一個(gè) OptionalInt :
OptionalInt maxCalories = menu.stream() .mapToInt(Dish::getCalories) .max();
現(xiàn)在,如果沒有最大值的話,你就可以顯式處理 OptionalInt 去定義一個(gè)默認(rèn)值了:
int max = maxCalories.orElse(1);數(shù)值范圍
和數(shù)字打交道時(shí),有一個(gè)常用的東西就是數(shù)值范圍。比如,假設(shè)你想要生成1和100之間的所有數(shù)字。Java 8引入了兩個(gè)可以用于 IntStream 和 LongStream 的靜態(tài)方法,幫助生成這種范圍:
range 和 rangeClosed 。這兩個(gè)方法都是第一個(gè)參數(shù)接受起始值,第二個(gè)參數(shù)接受結(jié)束值。但
range 是不包含結(jié)束值的,而 rangeClosed 則包含結(jié)束值。讓我們來看一個(gè)例子:
// 一個(gè)從1到100的偶數(shù)流 包含結(jié)束值 IntStream evenNumbers = IntStream.rangeClosed(1, 100) .filter(n -> n % 2 == 0); // 從1到100共有50個(gè)偶數(shù) System.out.println(evenNumbers.count());
這里我們用了 rangeClosed 方法來生成1到100之間的所有數(shù)字。它會(huì)產(chǎn)生一個(gè)流,然后你
可以鏈接 filter 方法,只選出偶數(shù)。到目前為止還沒有進(jìn)行任何計(jì)算。最后,你對(duì)生成的流調(diào)
用 count 。因?yàn)?count 是一個(gè)終端操作,所以它會(huì)處理流,并返回結(jié)果 50 ,這正是1到100(包括
兩端)中所有偶數(shù)的個(gè)數(shù)。請(qǐng)注意,比較一下,如果改用 IntStream.range(1, 100) ,則結(jié)果
將會(huì)是 49 個(gè)偶數(shù),因?yàn)?range 是不包含結(jié)束值的。
希望到現(xiàn)在,我們已經(jīng)讓你相信,流對(duì)于表達(dá)數(shù)據(jù)處理查詢是非常強(qiáng)大而有用的。到目前為
止,你已經(jīng)能夠使用 stream 方法從集合生成流了。此外,我們還介紹了如何根據(jù)數(shù)值范圍創(chuàng)建
數(shù)值流。但創(chuàng)建流的方法還有許多!本節(jié)將介紹如何從值序列、數(shù)組、文件來創(chuàng)建流,甚至由生成函數(shù)來創(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è)空流,如下所示:
Stream由數(shù)組創(chuàng)建流emptyStream = Stream.empty();
我們可以使用靜態(tài)方法 Arrays.stream 從數(shù)組創(chuàng)建一個(gè)流。它接受一個(gè)數(shù)組作為參數(shù)。例如,
我們可以將一個(gè)原始類型 int 的數(shù)組轉(zhuǎn)換成一個(gè) IntStream ,如下所示:
int[] numbers = {2, 3, 5, 7, 11, 13}; // 總和41 int sum = Arrays.stream(numbers).sum();
Java中用于處理文件等I/O操作的NIO API(非阻塞 I/O)已更新,以便利用Stream API。
java.nio.file.Files 中的很多靜態(tài)方法都會(huì)返回一個(gè)流。例如,一個(gè)很有用的方法是
Files.lines ,它會(huì)返回一個(gè)由指定文件中的各行構(gòu)成的字符串流。使用我們迄今所學(xué)的內(nèi)容,我們可以用這個(gè)方法看看一個(gè)文件中有多少各不相同的詞:
long uniqueWords; try (Streamlines = Files.lines(Paths.get(ClassLoader.getSystemResource("data.txt").toURI()), Charset.defaultCharset())) { uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) .distinct() .count(); System.out.println("uniqueWords:" + uniqueWords); } catch (IOException e) { e.fillInStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); }
你可以使用 Files.lines 得到一個(gè)流,其中的每個(gè)元素都是給定文件中的一行。然后,你
可以對(duì) line 調(diào)用 split 方法將行拆分成單詞。應(yīng)該注意的是,你該如何使用 flatMap 產(chǎn)生一個(gè)扁平的單詞流,而不是給每一行生成一個(gè)單詞流。最后,把 distinct 和 count 方法鏈接起來,數(shù)數(shù)流中有多少各不相同的單詞。
Stream API提供了兩個(gè)靜態(tài)方法來從函數(shù)生成流: Stream.iterate 和 Stream.generate 。
這兩個(gè)操作可以創(chuàng)建所謂的無限流:不像從固定集合創(chuàng)建的流那樣有固定大小的流。由 iterate和 generate 產(chǎn)生的流會(huì)用給定的函數(shù)按需創(chuàng)建值,因此可以無窮無盡地計(jì)算下去!一般來說,應(yīng)該使用 limit(n) 來對(duì)這種流加以限制,以避免打印無窮多個(gè)值。
1.迭代
我們先來看一個(gè) iterate 的簡單例子,然后再解釋:
Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println);
iterate 方法接受一個(gè)初始值(在這里是 0 ),還有一個(gè)依次應(yīng)用在每個(gè)產(chǎn)生的新值上的
Lambda( UnaryOperator
2.生成
與 iterate 方法類似, generate 方法也可讓你按需生成一個(gè)無限流。但 generate 不是依次
對(duì)每個(gè)新生成的值應(yīng)用函數(shù)的。它接受一個(gè) Supplier
看一個(gè)簡單的用法:
Stream.generate(Math::random) .limit(5) .forEach(System.out::println);
這段代碼將生成一個(gè)流,其中有五個(gè)0到1之間的隨機(jī)雙精度數(shù)。例如,運(yùn)行一次得到了下面
的結(jié)果:
0.8404010101858976 0.03607897810804739 0.025199243727344833 0.8368092999566692 0.14685668895309267
Math.Random 靜態(tài)方法被用作新值生成器。同樣,你可以用 limit 方法顯式限制流的大小,
否則流將會(huì)無限長。
你可能想知道, generate 方法還有什么用途。我們使用的供應(yīng)源(指向 Math.random 的方
法引用)是無狀態(tài)的:它不會(huì)在任何地方記錄任何值,以備以后計(jì)算使用。但供應(yīng)源不一定是無狀態(tài)的。你可以創(chuàng)建存儲(chǔ)狀態(tài)的供應(yīng)源,它可以修改狀態(tài),并在為流生成下一個(gè)值時(shí)使用。
我們?cè)谶@個(gè)例子中會(huì)使用 IntStream 說明避免裝箱操作的代碼。 IntStream 的 generate 方
法會(huì)接受一個(gè) IntSupplier ,而不是 Supplier
IntStream ones = IntStream.generate(() -> 1);
還記得第三章的筆記中,Lambda允許你創(chuàng)建函數(shù)式接口的實(shí)例,只要直接內(nèi)聯(lián)提供方法的實(shí)
現(xiàn)就可以。你也可以像下面這樣,通過實(shí)現(xiàn) IntSupplier 接口中定義的 getAsInt 方法顯式傳遞一個(gè)對(duì)象(雖然這看起來是無緣無故地繞圈子,也請(qǐng)你耐心看):
IntStream twos = IntStream.generate(new IntSupplier(){ @Override public int getAsInt(){ return 2; } });
generate 方法將使用給定的供應(yīng)源,并反復(fù)調(diào)用 getAsInt 方法,而這個(gè)方法總是返回 2 。
但這里使用的匿名類和Lambda的區(qū)別在于,匿名類可以通過字段定義狀態(tài),而狀態(tài)又可以用
getAsInt 方法來修改。這是一個(gè)副作用的例子。我們迄今見過的所有Lambda都是沒有副作用的;它們沒有改變?nèi)魏螤顟B(tài)。
這一章的東西很多,收獲也很多!現(xiàn)在你可以更高效地處理集合了。事實(shí)上,流讓你可以簡潔地表達(dá)復(fù)雜的數(shù)據(jù)處理查詢。此外,流可以透明地并行化。以下是我們應(yīng)從本章中學(xué)到的關(guān)鍵概念。
這一章的讀書筆記中,我們學(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é)果,例如求和或查找最大元素。
filter 和 map 等操作是無狀態(tài)的,它們并不存儲(chǔ)任何狀態(tài)。 reduce 等操作要存儲(chǔ)狀態(tài)才能計(jì)算出一個(gè)值。 sorted 和 distinct 等操作也要存儲(chǔ)狀態(tài),因?yàn)樗鼈冃枰蚜髦械乃性鼐彺嫫饋聿拍芊祷匾粋€(gè)新的流。這種操作稱為有狀態(tài)操作。
流有三種基本的原始類型特化: IntStream 、 DoubleStream 和 LongStream 。它們的操作也有相應(yīng)的特化。
流不僅可以從集合創(chuàng)建,也可從值、數(shù)組、文件以及 iterate 與 generate 等特定方法創(chuàng)建。
無限流是沒有固定大小的流。
代碼Github: chap5
Gitee: chap5
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77057.html
摘要:跳過元素流還支持方法,返回一個(gè)扔掉了前個(gè)元素的流。歸約到目前為止,我們見到過的終端操作都是返回一個(gè)之類的或?qū)ο蟮取_@樣的查詢可以被歸類為歸約操作將流歸約成一個(gè)值。通過反復(fù)使用加法,你把一個(gè)數(shù)字列表歸約成了一個(gè)數(shù)字。 使用流 在上一篇的讀書筆記中,我們已經(jīng)看到了流讓你從外部迭代轉(zhuǎn)向內(nèi)部迭代。這樣,你就用不著寫下面這樣的代碼來顯式地管理數(shù)據(jù)集合的迭代(外部迭代)了: /** * 菜單 ...
摘要:比如,你可以建立一個(gè),選出熱量超過卡路里的頭三道菜請(qǐng)注意也可以用在無序流上,比如源是一個(gè)。跳過元素流還支持方法,返回一個(gè)扔掉了前個(gè)元素的流。一般來說,應(yīng)該使用來對(duì)這種流加以限制,以避免打印無窮多個(gè)值。 一、篩選和切片 1.用謂詞篩選 Streams接口支持filter方法。該操作會(huì)接受一個(gè)謂詞(一個(gè)返回boolean的函數(shù))作為參數(shù),并返回一個(gè)包括所有符合謂詞的元素的流。例如篩選出所有...
摘要:實(shí)戰(zhàn)讀書筆記第一章從方法傳遞到接著上次的,繼續(xù)來了解一下,如果繼續(xù)簡化代碼。去掉并且生成的數(shù)字是萬,所消耗的時(shí)間循序流并行流至于為什么有時(shí)候并行流效率比循序流還低,這個(gè)以后的文章會(huì)解釋。 《Java8實(shí)戰(zhàn)》-讀書筆記第一章(02) 從方法傳遞到Lambda 接著上次的Predicate,繼續(xù)來了解一下,如果繼續(xù)簡化代碼。 把方法作為值來傳遞雖然很有用,但是要是有很多類似與isHeavy...
摘要:內(nèi)部迭代與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的。流只能遍歷一次請(qǐng)注意,和迭代器類似,流只能遍歷一次。 流(Stream) 流是什么 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級(jí)迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼了!我會(huì)在后面的筆記中...
摘要:使用流收集數(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 ...
閱讀 1971·2021-09-09 09:33
閱讀 1111·2019-08-30 15:43
閱讀 2657·2019-08-30 13:45
閱讀 3304·2019-08-29 11:00
閱讀 853·2019-08-26 14:01
閱讀 3568·2019-08-26 13:24
閱讀 477·2019-08-26 11:56
閱讀 2686·2019-08-26 10:27