
1.jpg
1.流
- 流是java API的新成員,它允許以聲明式的方式處理數(shù)據(jù)集合(通過(guò)查詢(xún)語(yǔ)句來(lái)表達(dá),而不是臨時(shí)編寫(xiě)一個(gè)實(shí)現(xiàn)),可以把流看成遍歷數(shù)據(jù)集的高級(jí)迭代器
- 流可以透明的并行處理
示例:
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName = menu.stream()
.filter(d -> d.getCalories() < 400) //選出400卡路里以下的菜肴
.sorted(comparing(Dish::getCalories)) //按照卡路里排序
.map(Dish::getName) //提取菜肴的名稱(chēng)
.limt(3) //截?cái)嗔?,使元素不超過(guò)給定數(shù)量
.collect(toList()); //將所有的名稱(chēng)保存在List中
如果想利用多核架構(gòu)并行執(zhí)行這段代碼,只需要把stream()換成parallelStream();
因?yàn)閒ilter,sorted,map,collect等操作是與具體線程無(wú)關(guān)的高層次組件,所以他們內(nèi)部實(shí)現(xiàn)可以是單線程的,也可能透明的充分利用電腦的多核架構(gòu);
2.流與集合
集合和流之間的差異就在于什么時(shí)候進(jìn)行計(jì)算
- 集合:集合是一個(gè)內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),它包含數(shù)據(jù)結(jié)構(gòu)中目前所有的值,集合中的每個(gè)元素都得先計(jì)算出來(lái)才能添加到集合中(你可以往集合中添加元素或者刪除元素,但是不管什么時(shí)候,集合中的每個(gè)元素都是放在內(nèi)存中的,元素都得先計(jì)算出來(lái)才能成為集合的一部分)(供應(yīng)商驅(qū)動(dòng))
- 流:流是在概念上固定的數(shù)據(jù)結(jié)構(gòu)(不能添加或者刪除元素)其元素按需計(jì)算,從另一個(gè)角度說(shuō),流就像是一個(gè)延遲創(chuàng)建的集合,只要在消費(fèi)者要求的時(shí)候才會(huì)計(jì)算值(需求驅(qū)動(dòng),實(shí)時(shí)制造)
2.1 流只能遍歷一次
3.流操作

QQ截圖20190419180918.png
- filter、map和Limit可以連成一條流水線,稱(chēng)為中間操作
- collect 觸發(fā)流水線執(zhí)行并且關(guān)閉它,稱(chēng)為終端操作,終端操作會(huì)從流水線生成結(jié)果,其結(jié)果是任何不是流的值,比如List,Integer甚至是void
long count = menu.stream()
.filter(d -> d.getCalories() > 300)
.distinct()
.limit(3)
.count(); //count操作返回的是一個(gè)long,不是流,所以是終端操作

QQ截圖20190419181827.png
4.使用流
4.1篩選、切片
- 篩選信息---外部迭代
List<Dish> vegetarianDishes = new ArrayList<>();
for(Dish d: menu){
if(d.isVegetarian()){
vegetarianDishes.add(d);
}
}
- 篩選信息---內(nèi)部迭代(Stream API)
import static java.util.stream.Collectors.toList;
List<Dish> vegetarianDishes = menu.stream().filter(Dish::isVegetarian).collect(toList());
- 流支持distinct方法,過(guò)濾重復(fù)數(shù)據(jù)
//篩選出列表中所有的偶數(shù),并確保沒(méi)有重復(fù)
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
- 截?cái)嗔?-流支持limit(n)方法,返回一個(gè)不超過(guò)給定長(zhǎng)度的流,所需的長(zhǎng)度作為參數(shù)傳遞費(fèi)limit
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300) //卡路里大于300
.limit(3) //符合卡路里大于300的前三個(gè)元素
.collect(toList()); //終端操作,流轉(zhuǎn)化為L(zhǎng)ist
- 跳過(guò)元素--流支持skip(n)方法,返回一個(gè)扔掉了前n個(gè)元素的流。如果元素不足n個(gè),則返回一個(gè)空流
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2) //跳過(guò)卡路里大于300的前兩個(gè)元素
.collect(toList());
4.2 映射
4.2.1 對(duì)流中每一個(gè)元素應(yīng)用函數(shù)
- 流支持Map方法,它會(huì)接受一個(gè)函數(shù)作為參數(shù)。這個(gè)函數(shù)會(huì)被應(yīng)用到每個(gè)元素上,并將其映射成一個(gè)新的元素
List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName) //map 提取名稱(chēng)
.map(String::length) // 返回 名稱(chēng)長(zhǎng)度
.collect(toList()); //終端操作,流轉(zhuǎn)化為L(zhǎng)ist
4.2.2 流的扁平化
- 使用flatMap方法--各個(gè)數(shù)組并不是分別映射成一個(gè)流,而是映射成流的內(nèi)容
/**
** 對(duì)于一張單詞表,如何返回一張列表,列出里面 各不相同的字符 呢?例如,給定單詞列表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]
**/
List<String> uniqueCharacters =
words.stream()
.map(w -> w.split("")) //將每個(gè)單詞轉(zhuǎn)換成由其字母構(gòu)成的數(shù)組
.flatMap(Arrays::stream) //將各個(gè)生成流扁平化為單個(gè)流
.distinct()
.collect(Collectors.toList());
4.3查找和匹配
Stream API提供了allMatch,anyMatch,noneMatch,findFirst和findAny方法匹配數(shù)據(jù)集中的某些元素
4.3.1 檢查謂詞是否至少匹配一個(gè)元素
- anyMatch方法--流中是否有一個(gè)元素能匹配給定的謂詞,該方法返回一個(gè)Boolean,因此是一個(gè)終端操作
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
4.3.2 檢查謂詞是否匹配所有元素
- allMatch方法的工作原理和anyMatch類(lèi)似,但它會(huì)看流中的元素是否都能匹配給定的謂詞
boolean isHealthy = menu.stream()
.allMatch(d -> d.getCalories() < 1000); //所有菜的熱量都低于1000卡路里 才返回true;
- noneMatch方法--確保流中沒(méi)有任何元素與給定的謂詞匹配
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories() >= 1000); //所有菜的熱量都低于1000卡路里 才返回true;
- anyMatch,allMatch和noneMatch這三個(gè)操作都用到了所謂的短路,相當(dāng)于java中&& 和 || 運(yùn)算符短路在流中的版本
4.3.3 查找元素
- findAny 方法將返回當(dāng)前流中的任意元素
Optional<Dish> dish = menu.stream()
.filter(Dish::isVegetarian) //篩選素菜
.findAny(); //找到一個(gè)立即結(jié)束流返回
- Optional<T>類(lèi)(java.util.Optional)是一個(gè)容器類(lèi),代表一個(gè)值存在或不存在。在上面的代碼中,findAny可能什么元素都沒(méi)找到
4.3.4 查找第一個(gè)元素
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst(); // 9
4.4 歸約
4.4.1元素求和
//reduce求和:第一個(gè)參數(shù)是求和初始值
List<Integer> numbers = Arrays.asList(5,7,9,12,45,67);
Integer total = numbers.stream().reduce(0, Integer::sum); //145
//流中沒(méi)有任何元素,無(wú)法返回和的情況
List<Integer> numbers11 = Arrays.asList();
Optional<Integer> total1 = numbers11.stream().reduce(Integer::sum); //Optional.empty
4.4.2最大值和最小值
List<Integer> numbers = Arrays.asList(5,7,9,12,45,67);
Optional<Integer> max = numbers.stream().reduce(Integer::max); //Optional[67]
Optional<Integer> min = numbers.stream().reduce(Integer::min); //Optional[5]
示例:求數(shù)組大小
List<Integer> numbers = Arrays.asList(5,7,9,12,45,67);
Integer ArrayTotal = numbers.stream().map(e -> 1).reduce(0,Integer::sum); // 6
long count = numbers.stream().count();
以上代碼有一個(gè)問(wèn)題,它有一個(gè)暗含的裝箱成本,每個(gè)Integer都必須拆箱成一個(gè)原始類(lèi)型,在進(jìn)行求和
4.5 數(shù)值流
4.5.1原始類(lèi)型流特化
java8引入了三個(gè)原始類(lèi)型特化流接口來(lái)解決拆箱問(wèn)題:IntStream,DoubleStream和LongStream,分別將流中的元素特化為int,long和double,從而避免了暗含的裝箱成本。
1.映射到數(shù)值流
將流轉(zhuǎn)換為特定版本的常用方法是mapToInt,mapToDouble,mapToLong,這些方法和前面說(shuō)的map方法的工作方式一樣,只是返回的是一個(gè)特化流,而不是Stream<T>
int calories = menu.stream() //返回一個(gè)Stream<dish>
.mapToInt(Dish::getCalories)
.sum();
````VUE