Java8新特性之使用流
阿新 • • 發佈:2019-01-03
之前我們介紹了流的概念和基本使用,同集合做了對比,基本對流有一個大概的瞭解了,接下來學習下如何使用流來完成工作。
一.篩選和切片
1.用謂詞篩選
Streams介面支援filter方法,該操作會接受一個謂詞(一個返回boolean的函式)作為引數,並返回一個包含所有謂詞的元素的流。例如篩選出所有素菜,建立一張素食選單:
List<Dish> vegetarianMenu = menu.Stream()
.filter( Dish:isVegetarian)
.collect( toList());
2.篩選各異的元素
流支援一個叫做distinct的方法,它會返回一個元素各異(根據流所生成元素的hashCode和equals方法實現)的流。例如,篩選列表中所有的偶數,並確保沒有重複:
3.截短流 流支援limit(n)方法,該方法會返回一個不超過給定長度的流。所需的長度作為引數傳遞給limit。如果流是有序的,則最多會返回前n個元素。例如,給出熱量超過300卡路里的前三道菜:List<Integer> numbers = Arrays. asList(1,2,1,3,3,2,4); numbers.stream().filter(n -> n%2 == 0).distinct().forEach(System.out ::println);
4.跳過元素 流還支援skip(n)方法,返回一個扔掉了前n個元素的流。如果流中元素不足n個,則返回一個空流。limit(n)和skip(n)是互補的。例如,跳過超過300卡路里的頭兩道菜,返回剩下的:List<Dish> dishes = menu.stream() .filter(n -> n.getCalories > 300) .limit(3) .collect( toList());
二.對映 一個非常常見的資料處理套路就是從某些物件中選擇資訊。比如在SQL裡,你可以從表中選擇一列。Stream API也通過map和flatMap方法提供了類似的工具。 1.對流中沒一個元素應用函式 流支援map方法,它會接受一個函式作為引數。這個函式會被應用到每個元素中,並將其對映成一個新的元素。 例如,給定一個單詞列表,返回另一個列表,顯示每個單詞中有幾個字母,需要對列表中的每個元素應用一個函式,應用的函式接受一個單詞,並返回其長度。List<Dish> dishes = menu.stream() .filter(n -> n.getCalories > 300) .skip(2) .collect( toList());
List<String> words = Arrays.asList("Java8", "Lambdas","In" ,"Action" );
List<Integer> wordLengths = words .stream()
.map(String::length)
.collect(toList());
練習:
給定一個數字列表,如何返回一個由每個數的平方構成的列表呢?如給定[1,2,3,4,5],應該返回[1,4,9,16,25]
List<Integer> numbers = Arrays.asList(1,2,3,4,5);
numbers.stream().map( n -> n*n ).forEach(System.out::println);
三.查詢和匹配
另一個常見的資料處理套路是看看資料集中的某些元素是否匹配一個給定的屬性。Stream API通過allMatch,anyMatch,noneMatch,findFirst和findAny方法提供這樣的工具。
1.檢查謂詞是否至少匹配一個元素
anyMatch方法可以回答"流中是否有一個元素能匹配給定的謂詞"。如,檢視選單裡面是否有素食可以選擇:
public static void anyMatchTest(){
if(menu .stream().anyMatch(Dish::isVegetarian)){
System. out.println("The menu is (somewhat) vegetarian friendly");
}
}
2.檢查謂詞是否匹配所有元素
allMatch方法和anyMatch類似,檢視流中的元素是否都能匹配給定的謂詞。如,看看菜品是否都有利於健康:
public static void allMatchTest(){
if(menu .stream().allMatch(d -> d.getCalories() < 1000)){
System. out.println("The menu is healthy friendly");
}
}
3.查詢元素
findAny方法將返回當前六中的任意元素。如找到一道素食菜餚:
public static void findAnyTest(){
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();
}
4.查詢第一個元素
findFirst用來找到第一個元素,如找到第一個平方能被3整除的整數:
public static void findFirstTest(){
List< Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream().map(n -> n*n ).filter(n -> n%3 == 0).findFirst();
}
四.規約
本節中,我們可以看到如何把一個流中的元素結合起來,使用reduce操作來表達更復雜的查詢,如"計算選單中的總卡路里"或"選單中卡路里最高的菜是哪一個"。這樣的查詢可以被歸類為規約操作。
1.元素求和
List<Integer> numbers = Arrays. asList(1,2,3,4,5);
int sum = numbers.stream().reduce(0, ( a, b) -> a+b );
reduce接受兩個引數,一個初始值,一個BinaryOperator<T>,這裡我們用的是(
a,
b) ->
a+b
2.最大值和最小值
public static void maxTest(){
List<Integer> numbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> max2 = numbers.stream().reduce((x ,y ) -> x < y ?x :y );
Optional<Integer> min = numbers.stream().reduce(Integer::min);
Optional<Integer> min2 = numbers.stream().reduce((x ,y ) -> x > y ?x :y );
}
五.構建流
本節介紹如何從值序列,陣列,檔案來建立流,甚至由生成函式來建立無限流。
1.由值建立流
public static void makeStreamByValue(){
Stream< String> stream = Stream.of("Java8", "Lambdas","In" ,"Action" );
stream.map(String::toUpperCase).forEach(System. out::println);
}
2.由陣列生成流
public static void makeStreamByArray(){
int []numbers = {2,3,5,7,11,13};
int sum = Arrays.stream( numbers).sum();
}
3.由函式生成流,建立無限流
Stream API 提供了靜態方法來從函式生成流,Stream.iterate和Stream.generate,這兩個操作可以建立無限流,不像從固定集合建立的流那樣有固定大小的流。一般來說 應該使用limit來對這種流加以限制,避免列印無窮多個值。
例如,生成一個所有正偶數的流:
public static void makeStreamByMethod(){
Stream.iterate (0, n -> n+2).limit(10).forEach(System.out::println);
}
練習,生成斐波那契數列
Stream.iterate(new int[]{0}, t -> new int []{t[1], t[0]+t [1]}).map(t -> t[0]).forEach(System.out::println);