Java 8の新しい特性の:ストリーム(一)


一.フローの概念
ストリームはJava APIの新しいメンバーで、一時的にインプリメンテーションを記述するのではなく、クエリ文で表現するデータセットを宣言的に処理できます.
Stream APIの機能:
--宣言:より簡潔で読みやすい;
--複合可能:より柔軟;
--並列可能:パフォーマンスが向上します.
    1.≪ストリームの定義|Streams Definition|oem_src≫:データ処理操作をサポートするソースから生成される要素のシーケンス.
ストリームには2つの重要な特徴があります.
--パイプライン:多くのストリーム操作自体が1つのストリームを返し、複数の操作がリンクされ、大きなパイプラインを形成することができます.
--内部反復:反復器を使用して明示的に反復する集合とは異なり、ストリームの反復操作は背後で行われます.
ケース:
public class Dish {
    private final String name;
    private final int calories;
    private final boolean vegetarian;
    private final DishType type;
    
    public Dish(String name, boolean vegetarian, int calories, DishType type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }
    
    public String getName() {
        return name;
    }
    
    public int getCalories() {
        return calories;
    }
    
    public boolean isVegetarian() {
        return vegetarian;
    }
}
public static void main(String[] args) {
    List menu = Arrays.asList(
        new Dish("pork", false, 800, DishType.MEAT),
        new Dish("prawns", false, 300, DishType.FISH),
        new Dish("chicken", false, 700, DishType.MEAT),
        new Dish("rice", true, 350, DishType.OTHER)
    );
    
    List threeHighCaloricDishNames = menu.stream() //   Stream
        .filter(d -> d.getCalories() > 300) //Stream
        .map(Dish::getName) //Stream
        .limit(3) //Stream
        .collect(toList()); //List,collect         
        
    System.out.println(threeHighCaloricDishNames);  //  :[pork, chicken, rice]
}

データソース:menu.ストリームに要素シーケンスを提供し、filter、map、limit、collectの一連のデータ処理操作を対流に適用します.collectを除いて、これらの操作はすべて1つのストリームを返します.これにより、ストリームラインに接続でき、ソースに対するクエリーと見なすことができます.最後にcollectは流水線の処理を開始し、結果を返します.collectを呼び出す前に、結果は発生せず、実際にはmenuから要素を選択しませんでした.チェーン内のメソッドはcollectが呼び出されるまで待機していると理解できます.
    2.フロー操作
ストリーム操作は、中間操作と端末操作に分けられる.接続可能なストリーム操作を中間操作,ストリームを閉じる操作を端末操作と呼ぶ.
≪中間操作|Intermediate Operations|emdw≫:中間操作は別のストリームを返します.これにより、複数の操作を接続してクエリーを形成できます.
中間操作は、フローライン上で終端操作がトリガーされない限り、何の処理も実行されません.これは,中間操作は一般に統合可能であり,端末操作時に一括してすべて処理するためである.
ターミナル操作:ターミナル操作は流れの流水線から結果を生成します.結果は、ストリームでない値です.
public static void main(String[] args) {
    List menu = Arrays.asList(
    new Dish("pork", false, 800, DishType.MEAT),
    new Dish("prawns", false, 300, DishType.FISH),
    new Dish("chicken", false, 700, DishType.MEAT),
    new Dish("rice", true, 350, DishType.OTHER)
    );
    
    List names = menu.stream()
        .filter(d -> {
            System.out.println("filtering " + d.getName());
            return d.getCalories() > 130;
        })
        .map(d -> {
            System.out.println("mapping " + d.getName());
            return d.getName();
        })
        .limit(3)
        .collect(toList());
        
    System.out.println(names);
}

    :
filtering pork
mapping pork
filtering prawns
mapping prawns
filtering chicken
mapping chicken
[pork, prawns, chicken]

上記の例によれば、filterとmapは2つの独立した操作であるにもかかわらず、同じ遍歴に統合されていることがわかる(この技術は循環結合と呼ばれる).
二.流れと集合の異同
集合とストリームの違いは、いつ計算されるかにあります.
≪コレクション|Assembly|emdw≫:データ構造の現在のすべての値を含むメモリ内のデータ構造です.コレクションに追加するには、コレクション内の各要素を先に計算する必要があります.
≪Streams|Streams|oem_src≫:コンセプト上固定されたデータ構造(要素の追加または削除はできません)であり、その要素は必要に応じて計算されます.
ストリームは、消費者が要求する場合にのみ値を計算する遅延作成の集合のようなものです.
    1.流れは一度だけ
ストリームは反復器と似ていて、一度しか遍歴できません.遍歴した後、このストリームが消費されていることを示します.
public static void main(String[] args) {
    List title = Arrays.asList("dispatch", "settlement", "wyvern");
    Stream s = title.stream();
    s.forEach(System.out::println);
    s.forEach(System.out::println); //         IllegalStateException: stream has already been operated upon or closed
}

    2.集合とストリームの遍歴データ方式の違い
Collectionインタフェースは、外部反復と呼ばれる反復(for-each)を行う必要があります.
Streamライブラリは内部反復を使用します.
内部反復の利点:プロジェクトは透明に並列処理できるか、より最適化された順序で処理できる.
内部反復の前提:filterやmapなどの反復を隠す操作リストを事前に定義しておきました.