摘要:分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組的鍵類型是,于是它最多可以分為兩組是一組,是一組。當(dāng)遍歷到流中第個(gè)元素時(shí),這個(gè)函數(shù)執(zhí)行時(shí)會(huì)有兩個(gè)參數(shù)保存歸約結(jié)果的累加器已收集了流中的前個(gè)項(xiàng)目,還有第個(gè)元素本身。
一、收集器簡(jiǎn)介
把列表中的交易按貨幣分組:
Map> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency));
從Collectors
類提供的工廠方法(例如groupingBy)創(chuàng)建的收集器。它們主要提供了三大功能:
將流元素歸約和匯總為一個(gè)值
元素分組
元素分區(qū)
二、歸約和匯總數(shù)一數(shù)菜單里有多少種菜:
long howManyDishes = menu.stream().collect(Collectors.counting());
這還可以寫得更為直接:
long howManyDishes = menu.stream().count();1.查找流中的最大值和最小值
可以使用兩個(gè)收集器,Collectors.maxBy和Collectors.minBy,來計(jì)算流中的最大或最小值。這兩個(gè)收集器接收一個(gè)Comparator參數(shù)來比較流中的元素.
找出菜單中熱量最高的菜:
Comparator2.匯總dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories); Optional mostCalorieDish = menu.stream() .collect(maxBy(dishCaloriesComparator));
Collectors.summingInt
它可接受一個(gè)把對(duì)象映射為求和所需int的函數(shù),并返回一個(gè)收集器;該收集器在傳遞給普通的collect方法后即執(zhí)行我們需要的匯總操作。
eg:
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
另外,Collectors.summingLong和Collectors.summingDouble方法的作用完全一樣,可以用于求和字段為long或double的情況。還有Collectors.averagingInt,連同對(duì)應(yīng)的averagingLong和averagingDouble可以計(jì)算數(shù)值的平均數(shù)。
summarizing操作
通過一次summarizing操作你可以就數(shù)出菜單中元素的個(gè)數(shù),并得到菜肴熱量總和、平均值、最大值和最小值:
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
這個(gè)收集器會(huì)把所有這些信息收集到一個(gè)叫作IntSummaryStatistics的類里,它提供了方便的取值(getter)方法來訪問結(jié)果。打印menuStatisticobject會(huì)得到以下輸出:
IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}
同樣,相應(yīng)的summarizingLong和summarizingDouble工廠方法有相關(guān)的LongSummaryStatistics和DoubleSummaryStatistics類型。
3.連接字符串joining工廠方法返回的收集器會(huì)把對(duì)流中每一個(gè)對(duì)象應(yīng)用toString方法得到的所有字符串連接成一個(gè)字符串。
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
joining工廠方法有一個(gè)重載版本可以接受元素之間的分界符
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));4.廣義的歸約匯總
可以用reducing方法創(chuàng)建的收集器來計(jì)算你菜單的總熱量,如下所示:
int totalCalories = menu.stream().collect(reducing( 0, Dish::getCalories, (i, j) -> i + j));
第一個(gè)參數(shù)是歸約操作的起始值。
第二個(gè)參數(shù)將菜肴轉(zhuǎn)換成一個(gè)表示其所含熱量的int。
第三個(gè)參數(shù)是一個(gè)BinaryOperator,將兩個(gè)項(xiàng)目累積成一個(gè)同類型的值。這里它就是對(duì)兩個(gè)int求和。
單參數(shù)形式的reducing來找到熱量最高的菜,如下所示:
OptionalmostCalorieDish = menu.stream().collect(reducing( (d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));
相比stream的reduce方法collect方法特別適合表達(dá)可變?nèi)萜魃系臍w約,更關(guān)鍵的是它適合并行操作
計(jì)算菜單里所有菜肴的卡路里總和,以不同的方法執(zhí)行同樣的操作:
第一種:
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));
第二種:
int totalCalories = menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();//reduce返回的是Optional
第三種:
int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();
最后一種最佳。
三、分組假設(shè)你要把菜單中的菜按照類型進(jìn)行分類,有肉的放一組,有魚的放一組,其他的都放另一組。用Collectors.groupingBy工廠方法返回的收集器就可以輕松地完成這項(xiàng)任務(wù),如下所示:
Map> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
其結(jié)果是下面的Map:
{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken]}
給groupingBy方法傳遞了一個(gè)Function(以方法引用的形式),它提取了流中每一道Dish的Dish.Type。我們把這個(gè)Function叫作分類函數(shù)
如果Dish中沒有定義類型獲取方法,可以使用lambda表達(dá)式:
public enum CaloricLevel { DIET, NORMAL, FAT } Map1.多級(jí)分組> dishesByCaloricLevel = menu.stream().collect( groupingBy(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; } ));
使用一個(gè)由雙參數(shù)版本的Collectors.groupingBy工廠方法創(chuàng)建的收集器,它除了普通的分類函數(shù)之外,還可以接受collector類型的第二個(gè)參數(shù):
Map>> dishesByTypeCaloricLevel = menu.stream().collect( groupingBy(Dish::getType, groupingBy(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; } ) ) );
這種多級(jí)分組操作可以擴(kuò)展至任意層級(jí),n級(jí)分組就會(huì)得到一個(gè)代表n級(jí)樹形結(jié)構(gòu)的n級(jí)Map2.按子組收集數(shù)據(jù)
傳遞給第一個(gè)groupingBy的第二個(gè)收集器可以是任何類型,而不一定是另一groupingBy
MaptypesCount = menu.stream().collect( groupingBy(Dish::getType, counting()));
其結(jié)果是下面的Map:
{MEAT=3, FISH=2, OTHER=4}
普通的單參數(shù)groupingBy(f)(其中f是分類函數(shù))實(shí)際上是groupingBy(f, toList())的簡(jiǎn)便寫法。
把收集器的結(jié)果轉(zhuǎn)換為另一種類型
查找每個(gè)子組中熱量最高的Dish
MapmostCaloricByType = menu.stream() .collect(groupingBy(Dish::getType, collectingAndThen( maxBy(comparingInt(Dish::getCalories)), //maxBy工廠方法生成的收集器的類型是Optional Optional::get)));
包裝的Optional沒什么用,把收集器返回的結(jié)果轉(zhuǎn)換為另一種類型,你可以使用Collectors.collectingAndThen工廠方法;返回的收集器groupingBy收集器只有在應(yīng)用分組條件后,第一次在流中找到某個(gè)鍵對(duì)應(yīng)的元素時(shí)才會(huì)把鍵加入分組Map中,所以O(shè)ptional::get這個(gè)操作放在這里是安全的,因?yàn)閞educing收集器永遠(yuǎn)都不會(huì)返回Optional.empty()
與groupingBy聯(lián)合使用的其他收集器的例子
MaptotalCaloriesByType = menu.stream().collect(groupingBy(Dish::getType, summingInt(Dish::getCalories)));
對(duì)于每種類型的Dish,菜單中都有哪些CaloricLevel。我們可以把groupingBy和mapping收集器結(jié)合起來,如下所示:
Map> caloricLevelsByType = menu.stream().collect( groupingBy(Dish::getType, mapping( dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; }, toSet() )));//生成的CaloricLevel流傳遞給一個(gè)toSet收集器, //它和toList類似,不過是把流中的元素累積到一個(gè)Set而不是List中,以便僅保留各不相同的值。
但通過使用toCollection,你就可以有更多的控制。例如,你可以給它傳遞一個(gè)構(gòu)造函數(shù)引用來要求HashSet:
Map四、分區(qū) 1.分區(qū)的優(yōu)勢(shì)> caloricLevelsByType = menu.stream().collect( groupingBy(Dish::getType, mapping( dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; }, toCollection(HashSet::new) )));
分區(qū)是分組的特殊情況:由一個(gè)謂詞(返回一個(gè)布爾值的函數(shù))作為分類函數(shù),它稱分區(qū)函數(shù)。分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組Map的鍵類型是Boolean,于是它最多可以分為兩組——true是一組,false是一組。例如,如果你是素食者或是請(qǐng)了一位素食的朋友來共進(jìn)晚餐,可能會(huì)想要把菜單按照素食和非素食分開:
Map> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
計(jì)算素食和非素食的數(shù)量:
menu.stream().collect(partitioningBy(Dish::isVegetarian, counting()));2.將數(shù)字按質(zhì)數(shù)和非質(zhì)數(shù)分區(qū)
public boolean isPrime(int candidate) { int candidateRoot = (int) Math.sqrt((double) candidate); return IntStream.rangeClosed(2, candidateRoot) .noneMatch(i -> candidate % i == 0); } public Map> partitionPrimes(int n) { return IntStream.rangeClosed(2, n).boxed() .collect( partitioningBy(candidate -> isPrime(candidate))); }
Collectors類的靜態(tài)工廠方法:
public interface Collector{ Supplier supplier(); BiConsumer accumulator(); Function finisher(); BinaryOperator combiner(); Set characteristics(); }
T是流中要收集的項(xiàng)目的泛型。
A是累加器的類型,累加器是在收集過程中用于累積部分結(jié)果的對(duì)象。
R是收集操作得到的對(duì)象(通常但并不一定是集合)的類型。
例如,你可以實(shí)現(xiàn)一個(gè)ToListCollector
public class ToListCollector1.理解 Collector 接口聲明的方法 (1)建立新的結(jié)果容器:supplier方法implements Collector , List >
在調(diào)用時(shí)它會(huì)創(chuàng)建一個(gè)空的累加器實(shí)例,供數(shù)據(jù)收集過程使用
public Supplier> supplier() { return () -> new ArrayList
(); }
或者使用構(gòu)造函數(shù)引用;
public Supplier(2)將元素添加到結(jié)果容器:accumulator方法> supplier() { return ArrayList::new; }
accumulator方法會(huì)返回執(zhí)行歸約操作的函數(shù)。當(dāng)遍歷到流中第n個(gè)元素時(shí),這個(gè)函數(shù)執(zhí)行時(shí)會(huì)有兩個(gè)參數(shù):保存歸約結(jié)果的累加器(已收集了流中的前 n?1 個(gè)項(xiàng)目),還有第n個(gè)元素本身。該函數(shù)將返回void,因?yàn)槔奂悠魇窃桓拢春瘮?shù)的執(zhí)行改變了它的內(nèi)部狀態(tài)以體現(xiàn)遍歷的元素的效果。對(duì)于ToListCollector,這個(gè)函數(shù)僅僅會(huì)把當(dāng)前項(xiàng)目添加至已經(jīng)遍歷過的項(xiàng)目的列表:
public BiConsumer, T> accumulator() { return (list, item) -> list.add(item); }
你也可以使用方法引用,這會(huì)更為簡(jiǎn)潔:
public BiConsumer(3)對(duì)結(jié)果容器應(yīng)用最終轉(zhuǎn)換:finisher方法, T> accumulator() { return List::add; }
在遍歷完流后,finisher方法必須返回在累積過程的最后要調(diào)用的一個(gè)函數(shù),以便將累加器對(duì)象轉(zhuǎn)換為整個(gè)集合操作的最終結(jié)果。
public Function(4) 合并兩個(gè)結(jié)果容器:combiner方法, List
> finisher() { return Function.identity(); //累加器對(duì)象恰好符合預(yù)期的最終結(jié)果, //因此無需進(jìn)行轉(zhuǎn)換。所以finisher方法只需返回identity函數(shù) }
combiner方法會(huì)返回一個(gè)供歸約操作使用的函數(shù),它定義了對(duì)流的各個(gè)子部分進(jìn)行并行處理時(shí),各個(gè)子部分歸約所得的累加器要如何合并。
public BinaryOperator> combiner() { return (list1, list2) -> { list1.addAll(list2); return list1; } }
有了這第四個(gè)方法,就可以對(duì)流進(jìn)行并行歸約了,會(huì)用到Java 7中引入的Fork/Join框架和Spliterator抽象
(5) characteristics方法Fork/Join是什么?
Fork/Join框架是Java7提供的并行執(zhí)行任務(wù)框架,思想是將大任務(wù)分解成小任務(wù),然后小任務(wù)又可以繼續(xù)分解,然后每個(gè)小任務(wù)分別計(jì)算出結(jié)果再合并起來,最后將匯總的結(jié)果作為大任務(wù)結(jié)果。其思想和MapReduce的思想非常類似。對(duì)于任務(wù)的分割,要求各個(gè)子任務(wù)之間相互獨(dú)立,能夠并行獨(dú)立地執(zhí)行任務(wù),互相之間不影響。Fork/Join的運(yùn)行流程圖如下:
我們可以通過Fork/Join單詞字面上的意思去理解這個(gè)框架。Fork是叉子分叉的意思,即將大任務(wù)分解成并行的小任務(wù),Join是連接結(jié)合的意思,即將所有并行的小任務(wù)的執(zhí)行結(jié)果匯總起來。
工作竊取算法
ForkJoin采用了工作竊取(work-stealing)算法,若一個(gè)工作線程的任務(wù)隊(duì)列為空沒有任務(wù)執(zhí)行時(shí),便從其他工作線程中獲取任務(wù)主動(dòng)執(zhí)行。為了實(shí)現(xiàn)工作竊取,在工作線程中維護(hù)了雙端隊(duì)列,竊取任務(wù)線程從隊(duì)尾獲取任務(wù),被竊取任務(wù)線程從隊(duì)頭獲取任務(wù)。這種機(jī)制充分利用線程進(jìn)行并行計(jì)算,減少了線程競(jìng)爭(zhēng)。但是當(dāng)隊(duì)列中只存在一個(gè)任務(wù)了時(shí),兩個(gè)線程去取反而會(huì)造成資源浪費(fèi)。工作竊取的運(yùn)行流程圖如下:
Fork/Join核心類
1.ForkJoinPool
ForkJoinPool是ForkJoin框架中的任務(wù)調(diào)度器,和ThreadPoolExecutor一樣實(shí)現(xiàn)了自己的線程池,提供了三種調(diào)度子任務(wù)的方法:
execute:異步執(zhí)行指定任務(wù),無返回結(jié)果;
invoke、invokeAll:同步執(zhí)行指定任務(wù),等待完成才返回結(jié)果;
submit:異步執(zhí)行指定任務(wù),并立即返回一個(gè)Future對(duì)象;
2.ForkJoinTask
Fork/Join框架中的實(shí)際的執(zhí)行任務(wù)類,有以下兩種實(shí)現(xiàn),一般繼承這兩種實(shí)現(xiàn)類即可。
RecursiveAction:用于無結(jié)果返回的子任務(wù);
RecursiveTask:用于有結(jié)果返回的子任務(wù);
Fork/Join框架實(shí)戰(zhàn)
下面實(shí)現(xiàn)一個(gè)Fork/Join小例子,從1+2+...10億,每個(gè)任務(wù)只能處理1000個(gè)數(shù)相加,超過1000個(gè)的自動(dòng)分解成小任務(wù)并行處理;并展示了通過不使用Fork/Join和使用時(shí)的時(shí)間損耗對(duì)比。import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class ForkJoinTask extends RecursiveTask{ private static final long MAX = 1000000000L; private static final long THRESHOLD = 1000L; private long start; private long end; public ForkJoinTask(long start, long end) { this.start = start; this.end = end; } public static void main(String[] args) { test(); System.out.println("--------------------"); testForkJoin(); } private static void test() { System.out.println("test"); long start = System.currentTimeMillis(); Long sum = 0L; for (long i = 0L; i <= MAX; i++) { sum += i; } System.out.println(sum); System.out.println(System.currentTimeMillis() - start + "ms"); } private static void testForkJoin() { System.out.println("testForkJoin"); long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); Long sum = forkJoinPool.invoke(new ForkJoinTask(1, MAX)); System.out.println(sum); System.out.println(System.currentTimeMillis() - start + "ms"); } @Override protected Long compute() { long sum = 0; if (end - start <= THRESHOLD) { for (long i = start; i <= end; i++) { sum += i; } return sum; } else { long mid = (start + end) / 2; ForkJoinTask task1 = new ForkJoinTask(start, mid); task1.fork(); ForkJoinTask task2 = new ForkJoinTask(mid + 1, end); task2.fork(); return task1.join() + task2.join(); } } } 這里需要計(jì)算結(jié)果,所以任務(wù)繼承的是RecursiveTask類。ForkJoinTask需要實(shí)現(xiàn)compute方法,在這個(gè)方法里首先需要判斷任務(wù)是否小于等于閾值1000,如果是就直接執(zhí)行任務(wù)。否則分割成兩個(gè)子任務(wù),每個(gè)子任務(wù)在調(diào)用fork方法時(shí),又會(huì)進(jìn)入compute方法,看看當(dāng)前子任務(wù)是否需要繼續(xù)分割成孫任務(wù),如果不需要繼續(xù)分割,則執(zhí)行當(dāng)前子任務(wù)并返回結(jié)果。使用join方法會(huì)阻塞并等待子任務(wù)執(zhí)行完并得到其結(jié)果。
程序輸出:
test 500000000500000000 4992ms -------------------- testForkJoin 500000000500000000 508ms需要特別注意的是:
ForkJoinPool 使用submit 或 invoke 提交的區(qū)別:invoke是同步執(zhí)行,調(diào)用之后需要等待任務(wù)完成,才能執(zhí)行后面的代碼;submit是異步執(zhí)行,只有在Future調(diào)用get的時(shí)候會(huì)阻塞。
這里繼承的是RecursiveTask,還可以繼承RecursiveAction。前者適用于有返回值的場(chǎng)景,而后者適合于沒有返回值的場(chǎng)景
這一點(diǎn)是最容易忽略的地方,其實(shí)這里執(zhí)行子任務(wù)調(diào)用fork方法并不是最佳的選擇,最佳的選擇是invokeAll方法。leftTask.fork(); rightTask.fork();替換為
invokeAll(leftTask, rightTask);具體說一下原理:對(duì)于Fork/Join模式,假如Pool里面線程數(shù)量是固定的,那么調(diào)用子任務(wù)的fork方法相當(dāng)于A先分工給B,然后A當(dāng)監(jiān)工不干活,B去完成A交代的任務(wù)。所以上面的模式相當(dāng)于浪費(fèi)了一個(gè)線程。那么如果使用invokeAll相當(dāng)于A分工給B后,A和B都去完成工作。這樣可以更好的利用線程池,縮短執(zhí)行的時(shí)間。
返回一個(gè)不可變的Characteristics集合,它定義了收集器的行為——尤其是關(guān)于流是否可以并行歸約,以及可以使用哪些優(yōu)化的提示。
Characteristics是一個(gè)包含三個(gè)項(xiàng)目的枚舉。
UNORDERED——?dú)w約結(jié)果不受流中項(xiàng)目的遍歷和累積順序的影響。
CONCURRENT——accumulator函數(shù)可以從多個(gè)線程同時(shí)調(diào)用,且該收集器可以并行歸約流。如果收集器沒有標(biāo)為UNORDERED,那它僅在用于無序數(shù)據(jù)源時(shí)才可以并行歸約。
IDENTITY_FINISH——這表明完成器方法返回的函數(shù)是一個(gè)恒等函數(shù),可以跳過。這種情況下,累加器對(duì)象將會(huì)直接用作歸約過程的最終結(jié)果。這也意味著,將累加器A不加檢查地轉(zhuǎn)換為結(jié)果R是安全的。
@Override public Set2.進(jìn)行自定義收集而不去實(shí)現(xiàn)Collectorcharacteristics() { return Collections.unmodifiableSet(EnumSet.of( IDENTITY_FINISH, CONCURRENT)); }
Stream有一個(gè)重載的collect方法可以接受另外三個(gè)函數(shù)——supplier、accumulator和combiner,其語義和Collector接口的相應(yīng)方法返回的函數(shù)完全相同。
Listdishes = menuStream.collect( ArrayList::new, List::add, List::addAll);//它永遠(yuǎn)都是一個(gè)IDENTITY_FINISH和CONCURRENT但并非UNORDERED的收集器。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74250.html
摘要:內(nèi)部迭代與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的。流只能遍歷一次請(qǐng)注意,和迭代器類似,流只能遍歷一次。 流(Stream) 流是什么 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級(jí)迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼了!我會(huì)在后面的筆記中...
摘要:但是,最好使用差異化的類型定義,函數(shù)簽名如下其實(shí)二者說的是同一件事。后者的返回值和初始函數(shù)的返回值相同,即。破壞式更新和函數(shù)式更新的比較三的延遲計(jì)算的設(shè)計(jì)者們?cè)趯⒁霑r(shí)采取了比較特殊的方式。四匹配模式語言中暫時(shí)并未提供這一特性,略。 一、無處不在的函數(shù) 一等函數(shù):能夠像普通變量一樣使用的函數(shù)稱為一等函數(shù)(first-class function)通過::操作符,你可以創(chuàng)建一個(gè)方法引用,...
摘要:第四章引入流一什么是流流是的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn)。 第四章 引入流 一、什么是流 流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級(jí)迭代器。此外,流還可以透明地并行處理,你無需寫任何多線程代碼。 下面兩段代碼都是用來返回低...
摘要:本章是該書的第五章主要講了方法引用和收集器方法引用形如這樣的表達(dá)式可以簡(jiǎn)寫為這種簡(jiǎn)寫的語法被稱為方法引用方法引用無需考慮參數(shù)因?yàn)橐粋€(gè)方法引用可以在不同的情況下解析為不同的表達(dá)式這依賴于的推斷方法引用的類型方法引用可以分為四類引用靜態(tài)方法 本章是該書的第五章, 主要講了方法引用和收集器 方法引用 形如: artist -> artist.getName() (String arg) ->...
摘要:流的使用一般包括三件事一個(gè)數(shù)據(jù)源來執(zhí)行一個(gè)查詢一個(gè)中間操作鏈,形成一條流水線一個(gè)終端操作,執(zhí)行流水線并生成結(jié)果以上便是流的一些基礎(chǔ)知識(shí),下一章會(huì)更加深入理解流。實(shí)戰(zhàn)第四章引入流讀書筆記歡迎加入咖啡館的春天。 流是什么 幾乎每個(gè) Java 應(yīng)用都會(huì)制造和處理集合。流 允許我們以聲明性方式處理集合(通過類似于 SQL 查詢語句來表達(dá),而不是臨時(shí)寫一個(gè)實(shí)現(xiàn))。此外 流 還可以透明地并行處理,...
摘要:新特性總覽標(biāo)簽本文主要介紹的新特性,包括表達(dá)式方法引用流默認(rèn)方法組合式異步編程新的時(shí)間,等等各個(gè)方面。還有對(duì)應(yīng)的和類型的函數(shù)連接字符串廣義的歸約匯總起始值,映射方法,二元結(jié)合二元結(jié)合。使用并行流時(shí)要注意避免共享可變狀態(tài)。 Java8新特性總覽 標(biāo)簽: java [TOC] 本文主要介紹 Java 8 的新特性,包括 Lambda 表達(dá)式、方法引用、流(Stream API)、默認(rèn)方...
閱讀 3641·2021-11-15 11:37
閱讀 2310·2021-09-24 10:39
閱讀 2423·2021-07-25 21:37
閱讀 1404·2019-08-30 15:56
閱讀 2574·2019-08-30 15:55
閱讀 943·2019-08-30 15:54
閱讀 2116·2019-08-30 14:21
閱讀 845·2019-08-30 11:24