天天看點

Java8(六)——Stream流的相關操作前言中間操作終端操作總結

前言

上一篇部落格中針對Stream流的建立做了一個簡單總結,這一篇部落格就簡單總結一下Stream流的操作。在《Java 8 in action》一書中對Stream流的操作總結成兩大類——中間操作和終端操作。

中間操作

中間操作——例如filter,sorted等即為中間操作,這些操作會傳回一個流,這些流類似于一個流水生産線上的中間操作,這些操作的傳回結果就是流。下面開始總結中間操作的一些執行個體

篩選和切片

filter

這裡還是用建立Stream部落格中的執行個體來繼續讨論相關問題,這裡說的篩選其實就是filter操作,如果我們要選出Dish菜單中的蔬菜,可以直接這樣操作。

public static void main(String[] args) {
    List<Dish> dishList = DishContainer.getDishList();
    //filter中接收的是Predicate接口
    List<Dish> result = dishList.stream().filter(Dish::isVegetarian).collect(toList());
    System.out.println(result);
}
           

filter的源碼如下所示:

Stream<T> filter(Predicate<? super T> predicate);
           

其實也就是接受一個Predicate接口,滿足要求的作為stream傳回,傳回類型沒有變化,是以為中間操作。

distinct

distinct這個類似資料庫中的distinct操作,其實就是去重,下面的執行個體可以說明distinct操作。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);
    List<Integer> collect = numbers.stream().filter(i -> i % 2 == 0).distinct().collect(toList());
    System.out.println(collect);
}
           

運作結果:

Java8(六)——Stream流的相關操作前言中間操作終端操作總結

結果中已經去除了重複的資料。

limit

該方法會傳回一個不超過指定長度的流,也類似于SQL語句中的limit操作。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);

    List<Integer> result = numbers.stream().filter(i -> i % 2 == 0).distinct().limit(2).collect(toList());
    System.out.println(result);
}
           

在上述代碼中,加入limit(2),最終傳回的結果就隻有兩個了

skip

skip和limit正好相反。下面的運作結果,與limit的結果互補。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);

    List<Integer> result = numbers.stream().filter(i -> i % 2 == 0).distinct().skip(2).collect(toList());
    System.out.println(result);
}
           

映射

映射相較于前面的操作,略微有點含量了。

map——對流中每一個元素應用函數

Stream流支援map方法,它會接受一個函數作為參數,這個函數會被應用到每個元素上,并将其映射成一個新的元素。

例如:我們隻需要擷取菜單中的所有猜的名字。不需要其他資訊,這個時候我們就可以用map來完成。

public static void main(String[] args) {
    List<Dish> dishes = DishContainer.getDishList();

    //map操作會映射到流中的每個元素上,一定程度上會決定傳回值的類型
    List<String> dishNames = dishes.stream().map(Dish::getName).collect(toList());
    System.out.println(dishNames);
}
           

flatMap——流的扁平化

一個需求:給定一個單詞清單,傳回這個單詞清單中各不相同的字元。例如,給定單詞清單["Hello","World"],你想要傳回清單["H","e","l","o","W","r","d"]

這個時候我們首先想到的就是利用map來完成,但是如果利用map(world->world.split("")).distinct()之後,這個傳回的是一個Stream<String[]>,但是我們真正需要的是Stream<String>,這個時候可以用flatMap的方式解決它。

public static void main(String[] args) {
    String[] strings = {"hello","world"};
    List<String> streams = Arrays.stream(strings).map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(toList());
    System.out.println(streams);
}
           

flatMap(Arrays::stream),使用flatMap方法的效果是,各個數組并不是分别映射成一個流,而是映射成流的内容。flatMap其實就是讓我們把一個流中的每個值都換成另一個流,然後把所有的流連接配接起來成為一個流。這裡還是單獨看另外一個執行個體吧。執行個體比較簡單,一看就懂。

/**
 * autor:liman
 * createtime:2019/9/1
 * comment:flatmapDemo
 */
public class StreamFlagMapDemo {

    public static void main(String args[]) {

        List<String> teamIndia = Arrays.asList("Virat", "Dhoni", "Jadeja");
        List<String> teamAustralia = Arrays.asList("Warner", "Watson", "Smith");
        List<String> teamEngland = Arrays.asList("Alex", "Bell", "Broad");
        List<String> teamNewZeland = Arrays.asList("Kane", "Nathan", "Vettori");
        List<String> teamSouthAfrica = Arrays.asList("AB", "Amla", "Faf");
        List<String> teamWestIndies = Arrays.asList("Sammy", "Gayle", "Narine");
        List<String> teamSriLanka = Arrays.asList("Mahela", "Sanga", "Dilshan");
        List<String> teamPakistan = Arrays.asList("Misbah", "Afridi", "Shehzad");

        List<List<String>> playersInWorldCup2016 = new ArrayList<>();
        playersInWorldCup2016.add(teamIndia);
        playersInWorldCup2016.add(teamAustralia);
        playersInWorldCup2016.add(teamEngland);
        playersInWorldCup2016.add(teamNewZeland);
        playersInWorldCup2016.add(teamSouthAfrica);
        playersInWorldCup2016.add(teamWestIndies);
        playersInWorldCup2016.add(teamSriLanka);
        playersInWorldCup2016.add(teamPakistan);

        // 在沒有java8 之前,我們針對這個集合需要周遊兩次,同時需要一個集合彙總
        List<String> listOfAllPlayers = new ArrayList<>();

        for(List<String> team : playersInWorldCup2016){
            for(String name : team){
                listOfAllPlayers.add(name);
            }
        }

        System.out.println("Players playing in world cup 2016");
        System.out.println(listOfAllPlayers);


        // flatMap會将所有的集合中的集合扁平化
        List<String> flatMapList = playersInWorldCup2016.stream()
                .flatMap(pList -> pList.stream())
                .collect(Collectors.toList());

        System.out.println("List of all Players using Java 8");
        System.out.println(flatMapList);
    }
}
           

終端操作

終端操作會從流的流水線生成結果,其結果并不是流,例如List、Integer甚至void。從下面開始,就都是終端操作了。

查找和比對

anyMatch

流中是否有一個元素比對給定的條件(謂詞),隻要有一個比對,就直接傳回true。

List<Dish> dishes = DishContainer.getDishList();
boolean hasVegetarian = dishes.stream().anyMatch(Dish::isVegetarian);
if(hasVegetarian){
    System.out.println("the menu is vegetarian friendly;");
}
           

allMatch

看流中所有的元素是否比對指定的謂詞。

boolean isHealthy = dishes.stream().allMatch(d -> d.getCalories() < 1000);
if(isHealthy){
    System.out.println("the menu calories is frendly;");
}
           

noneMatch類似,這裡就不再解釋了。

findAny與findFirst

findAny——将傳回目前流中的任意一個元素,findFirst——出現排序後的第一個元素。

這裡直接上執行個體吧,異常簡單。

dishes.stream().filter(Dish::isVegetarian).findFirst().ifPresent(d-> System.out.println(d.getName()));
           
dishes.stream().filter(Dish::isVegetarian).findAny().ifPresent(d-> System.out.println(d.getName()));
           

歸約

歸約一定程度上也是查詢操作,這類查詢操作需要将流中的所有元素反複結合起來,最終得到一個值。直接上執行個體吧,真心可以解釋的不多。reduce操作,Lambda反複結合每個元素,直到流被歸約成一個值。

public class StreamReduceDemo {

    public static void main(String[] args) {
        //元素求和
        Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Integer result = stream.reduce(0, (i, j) -> i + j);
        System.out.println(result);

        //最大值和最小值
        Stream<Integer> numStream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Optional<Integer> maxNum = numStream.reduce(Integer::max);
        System.out.println(maxNum.get());

        Stream<Integer> minStream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Optional<Integer> minNum = minStream.reduce(Integer::min);
        System.out.println(minNum.get());
        
    }
}
           

這裡還是貼出一個reduce的詳細操作,這個圖直接來自《Java8 in action》

Java8(六)——Stream流的相關操作前言中間操作終端操作總結

總結

總之,區分中間操作和終端操作就是看傳回類型是不是一個流,其實區分這兩個操作并沒有那麼重要。隻是針對stream中的操作進行了一些簡單介紹,下一篇部落格會主要總結一個執行個體。