Java8實戰 — 引入流

流是什麼

    流是Java API的新成員,它容許你以聲明的方式處理數據集合,簡單來講,能夠把它看成數據集的高級迭代器。此外,流還能夠透明地並行處理,你無需寫任何多線程代碼了。java

    舉個例子來講明流的好處,有一個簡單的場景,要求返回低熱量的菜餚名稱,並按照卡路里排序,實體代碼以下:
 數據庫

package cn.net.bysoft.chapter4;

public class Dish {
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    @Override
    public String toString() {
        return name;
    }

    public enum Type {
        MEAT, FISH, OTHER
    }
}
package cn.net.bysoft.chapter4;

import java.util.Arrays;
import java.util.List;

public class Restaurant {
    private List<Dish> menu = Arrays.asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT), 
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER), 
            new Dish("rice", true, 350, Dish.Type.OTHER),
            new Dish("season fruit", true, 120, Dish.Type.OTHER), 
            new Dish("pizza", true, 550, Dish.Type.OTHER),
            new Dish("prawns", false, 300, Dish.Type.FISH), 
            new Dish("salmon", false, 450, Dish.Type.FISH));

    public List<Dish> getMenu() {
        return menu;
    }

    public void setMenu(List<Dish> menu) {
        this.menu = menu;
    }

}

    先用Java7寫一次:編程

// 餐廳對象
        Restaurant restaurant = new Restaurant();

        // 返回低熱量的菜餚名稱,並按照卡路里排序。
        // Java7寫法
        List<Dish> lowCaloricDishes = new ArrayList<>();
        for (Dish d : restaurant.getMenu()) {
            if (d.getCalories() < 400)
                lowCaloricDishes.add(d);
        }

        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {

            @Override
            public int compare(Dish d1, Dish d2) {
                return Integer.compare(d1.getCalories(), d2.getCalories());
            }

        });
        
        List<String> lowCaloricDishesName = new ArrayList<>();
        for(Dish d : lowCaloricDishes) 
            lowCaloricDishesName.add(d.getName());

    這段代碼中,用了一個「垃圾變量」lowCaloricDishes,它惟一的做用就是做爲一次性的中間容器,在Java8中,實現的細節被放在它本該歸屬的庫中:數組

// 餐廳對象
        Restaurant restaurant = new Restaurant();
        
        // Java8寫法
        List<String> threeHighCaloricDishNames = restaurant.getMenu().stream()
                .filter(d -> d.getCalories() > 300)
                .sorted(comparing(Dish::getCalories))
                .map(Dish::getName)
                .limit(3)
                .collect(toList());

    利用多核架構並行執行這段代碼只須要吧stream()換成parallelStream()就能夠了。多線程

    Java8中的Stream API可讓你寫出的這樣的代碼:架構

  • 聲明性 —— 更簡潔、更易讀;
  • 可複合 —— 更靈活;
  • 可並行 —— 性能更好;

流簡介

    流的簡短定義是:從支持數據處理操做的源生成的元素序列。編程語言

  • 元素序列 —— 流提供了一個接口,能夠訪問特定元素類型的一組有序值;
  • 源 —— 流會使用一個提供數據的源,如集合、數組或輸入/輸出資源;
  • 數據處理操做 —— 流的數據處理功能支持相似於數據庫的操做,以及函數式編程語言中的經常使用操做;
  • 流水線 —— 不少流操做自己會返回一個流,多個操做就能夠鏈接起來;
  • 內部迭代 —— 流的迭代操做是在背後進行的;

    從上面的代碼中可以體現上面的概念:ide

  • filter —— 接受Lambda,從流中排除某些元素;
  • map —— 接收Lambda,將元素轉換成其餘形式或提取數據;
  • limit —— 截斷流;
  • collect —— 將流轉換爲其餘形式;

流與集合的差異

只能遍歷一次

    流只能遍歷一次,遍歷完以後,就說這個流已經消費掉了。能夠從原始數據源在得到一個新的流來從新遍歷一邊,就像迭代器同樣。下面的操做會拋出一個異常:函數式編程

package cn.net.bysoft.chapter4;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Example2 {
    public static void main(String[] args) {
        List<String> title = Arrays.asList("Java8", "In", "Action");
        Stream<String> s = title.stream();
        s.forEach(System.out::println);
        s.forEach(System.out::println);
    }
}
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(Unknown Source)
	at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source)
	at cn.net.bysoft.chapter4.Example2.main(Example2.java:12)

外部迭代與內部迭代

    使用Collection接口須要用戶去迭代,這稱爲外部迭代。相反,Streams庫使用內部迭代——它幫你把迭代作了,還把獲得的流值存在了某個地方,只要給出一個函數說要幹什麼就能夠了。例如:函數

// 餐廳對象
        Restaurant restaurant = new Restaurant();
        
        // 使用for-each循環作外部迭代
        List<String> names1 = new ArrayList<>();
        for(Dish d : restaurant.getMenu()) 
            names1.add(d.getName());
        
        // 使用迭代器作外部迭代
        List<String> names2 = new ArrayList<>();
        Iterator<Dish> iterator = restaurant.getMenu().iterator();
        while(iterator.hasNext()) {
            Dish d = iterator.next();
            names2.add(d.getName());
        }
        
        // 使用流作內部迭代
        List<String> names3 = restaurant.getMenu().stream()
                .map(Dish::getName)
                .collect(toList());

流操做

    java.util.stream.Stream中的Stream接口定義了許多操做,能夠分爲兩大類:中間操做和終端操做。

操做 類型 返回 參數 描述
filter 中間 Stream<T> Predicate<T> T -> boolean
map 中間 Stream<R> Function<T,R> T -> R
limit 中間 Stream<T>    
sorted 中間 Stream<T> Comparator<T> (T,T) -> int
distint 中間 Stream<T>    
forEach 終端      
count 終端      
collect 終端      

    諸如filter或sorted等中間操做會返回另外一個流。這讓多個操做能夠鏈接起來造成一個查詢。

    終端操做會從流的流水線生成結果。

    總而言之,流的使用通常包括三件事:

  • 一個數據源來執行一個查詢;
  • 一箇中間操做鏈造成一條流水線;
  • 一個終端操做執行流水線,並生成結果;
相關文章
相關標籤/搜索