TIL| Java Java Stream


航海99の2週目のアルゴリズム週間で、与えられた問題を解きながら、他の人の解答を観察してみると、よく使われるものがいくつかあることに気づき、その一つJavaのStreamを学びたいと思っていました.

ストリーム(Stream)


Java StreamはJava 8からサポートされている機能で、集合に格納されている要素を迂回して処理できるコードモードです.ストリームの前に、forfoe-each文を使用して配列または集合インスタンスの要素を処理できますが、論理が複雑であればあるほどコードが長くなるか、ループが多くなります.
逆に、Streamは「データストリーム」であり、配列、集合で関数を組み合わせ、必要な値を加工またはフィルタリングし、Ramdaを使用してコードを簡潔に表現することができます.

理解する内容

  • 作成-Streamインスタンスの作成
  • 加工-Filter、Map、Sond、Peak(中間操作)
  • 作成結果-端末操作
  • 作成


    配列と集合に加えて、複数の方法でStreamを作成することもできます.

    1.Streamの配列


    配列は、Arrays.stream()メソッドパラメータに配列を入力することによって配列を巡回するStreamオブジェクトを作成することができる.
    String[] arr = new String[]{"a", "b", "c"};
    Stream<String> stream1 = Arrays.stream(arr);
    Stream<String> stream2 = Arrays.stream(arr, 1, 3); // idx 1포함 3 제외 [b, c]

    2.コレクションStream


    Collection、List、Setなどの集合タイプでは、stream()メソッドを使用してStreamオブジェクトを作成できます.
    List<String> list = Arrays.asList("a", "b", "c");
    Stream<String> stream = list.stream();

    3. Stream builder


    配列/集合ではなくコンストラクタを使用して、Streamに直接値を入れることができます.addに値を追加し、メソッドを使用してStreamを返すことができます.
    Stream<String> builderStream = Stream.<String>builder()
        							.add("Eric")
                                    .add("Elena")
                                    .add("Java")
       								.build(); // [Eric, Elena, Java]

    4. Stream generate

    build()に相当するランダを使用してStreamを作成します.
    Supplier-パラメータがなく、戻り値のみの関数インタフェースです.Supplier<T>でreturn値が生成されます.
    public static<T> Stream<T> generate(Supplier<T> s) { ... }  
    Stream.generateは無限のStreamになるので、Streamを生成するにはサイズを制限する必要があります.
    Stream<String> stream = Stream.generate(() -> "hyemco").limit(3);
    limit()を使用して、3つの「hyemco」を含むStreamを生成します.

    5.基本金Stream

    Supplier<T>を使用することもできますが、リストまたは配列を使用して基本タイプ(int、long、double)Streamを直接作成できます.
    IntStream stream = IntStream.range(1, 3)  // [1, 2]
    LongStream stream = LongStream.raneClosed(1, 3)  // [1, 2, 3]
    generaterange()の範囲が異なり、後者は終了因子を含む範囲を生成する.

    ++接続Stream

    rangeClosed()では、2つのStreamを接続して新しいStreamオブジェクトを作成できます.
    Stream<String> stream1 = Stream.of("one", "two", "three");
    Stream<String> stream2 = Stream.of("하나", "둘", "셋");
    Stream<String> concat = Stream.concat(stream1, stream2);
    // [one, two, three, 하나, 둘, 셋]

    加工


    生成されたStreamオブジェクトを使用してStream内の要素を加工し、必要な要素のみを抽出できます.これらのプロセスは中間操作(intermediate operations)と呼ばれ、操作が完了するとStreamオブジェクトが返されるため、複数の加工操作を一度にリンクすることができます.

    1. Filter


    フィルタ(Filter)は、Streamオブジェクト内の各要素を評価し、必要な基準を満たす要素をフィルタします.
    boolean値を返す関数型インタフェースStream.concatを使用して、true値のみを返します.
    Stream<Integer> stream = IntStream.range(1, 10);
    stream.filter(v -> (v % 2 == 0).forEach(System.out::prrintln);
    // [2, 4, 6, 8]
    1から9までのStreamを生成し,filter法に偶数をスクリーニングしたRam茶食を加え,1~9で偶数元素のみがstreamオブジェクトに戻る.

    2. Map


    マッピング(Map)は、ストリームオブジェクトの要素を特定の値に変換するためのパラメータとしてラムダ式を受け入れます.
    List<String> names = Arrays.asList("Eden", "Jin", "Harry");
    Stream<String> stream = names.stream().map(String::toUpperCase);
    // [ERIC, JIN, HARRY]
    map()のようなPredicateMethodもあり、これはパラメータ形式で受信されたラムダ式戻りタイプStreamである.通常、オーバーラップ構造Streamを「フラット化」(Flatting)と呼ばれる別個のStreamとして作成するために使用されます.
    List<List<String>> list = 
    Arrays.asList(Arrays.asList("a", "b", "c"), 
                  Arrays.asList("가", "나", "다"));
    // [[a, b, c], [가, 나, 다]]
    上のネスト構造のリストは、flatMap()でネスト構造を削除します.
    List<String> flatList = list.stream().flatMap(Collection::stream)
    									 .collect(Collectiors.toList());
    // [a, b, c, 가, 나, 다]

    3. Sorted


    Streamオブジェクト内の要素をソートする方法.
    他のソートと同様に、Comparatorの方法を使用します.
    Stream<T> sorted();
    Stream<T> sorted(Comparator<? super T> comparator);
    1)パラメータなし呼び出し-昇順ソート
    IntStream.of(11, 7, 24, 2).sorted().boxed().collect(Collectors.toList());
    // [2, 7, 11, 24]
    2)ファクタの順序を超えた場合
    List<String> lang = 
      Arrays.asList("Java", "Scala", "Groovy", "Python", "Go", "Swift");
    
    lang.stream()
      .sorted()
      .collect(Collectors.toList());
    // [Go, Groovy, Java, Python, Scala, Swift]
    
    
    // Comparator로 알파벳 역순으로 정렬
    lang.stream()
      .sorted(Comparator.reverseOrder())
      .collect(Collectors.toList());
    // [Swift, Scala, Python, Java, Groovy, Go]

    4. Peak


    ラムダ式Consumerをパラメータとして受け入れるので、元素を加工しても返さない方法です.
    Stream<T> peek(Consumer<? super T> action);
    したがって,Stream内の演算では,中間過程の結果を調べるためによく用いられる.
    int sum = IntStream.of(1, 3, 5, 7, 9)
      .peek(System.out::println)
      .sum();

    結果の作成


    以上では、Streamを加工して別のStreamオブジェクトに戻す中間操作(Intermediate Operation)、加工要素を出力したり、他のアレイ/セットに送信したりする最終操作(Terminal Operation)を行う必要があります.

    1. Collecting

    flatMapメソッドは、collectオブジェクトによって提供されるCollectorタイプのパラメータを受け入れて処理する.
    以下の例があります.
    List<Product> productList = 
      Arrays.asList(new Product(23, "potatoes"),
                    new Product(14, "orange"),
                    new Product(13, "lemon"),

    Collectors.toList()


    Streamで作業した結果がリストに戻ります.
    List<String> list =
      productList.stream()
        .map(Product::getName)
        .collect(Collectors.toList());
    // [potatoes, orange, lemon]

    Collectors.joining()


    Streamで動作した結果をStringに接続します.
    String listToString = 
     productList.stream()
      .map(Product::getName)
      .collect(Collectors.joining());
    // potatoesorangelemon
    
    String listToString = 
     productList.stream()
      .map(Product::getName)
      .collect(Collectors.joining(", ", "<", ">"));
    // <potatoes, orange, lemon, bread, sugar>
    Collectorsの最初のパラメータ(区切り記号)は要素区切り記号であり、2番目のパラメータ(prefix)は戻り値の一番前の文字であり、3番目のパラメータ(接尾辞)は最後の文字である.
    ++
    それ以外にも、以下の方法があります.
  • Collectors.AVeragingInt()-数値を求める平均値
  • Collectors.和()-数値の和を求める
  • Collectors.要約図()-数値を求める個数、和、平均値、最大値および最小値
  • Collectors.groupingBy()-特定の条件で要素をグループ化
  • Collectors.パーティションBy()-特定の条件で要素をグループ化(boolen値を返す)
  • Collectors.collectingAndThen()-collectの後に他の操作を実行する必要があります
  • Collectors.of()-joiningカスタム
  • 2.数値の統計

    collectorおよびsumは、Streamが空の場合に0を出力する.最高価格で、最高価格でオプションを利用して返却します.
    int sum = IntStream.of(2, 4, 6, 8, 10).sum();
    int count = IntStream.of(2, 4, 6, 8, 10).count();
    int average = IntStream.of(2, 4, 6, 8, 10).average();
    
    OptionalInt min = IntStream.of(2, 4, 6, 8, 10).min();
    OptionalInt max = IntStream.of(2, 4, 6, 8, 10).max();

    3. Reduce

    countメソッドには3つのパラメータがあります.
  • アキュムレータ-Stream要素が来るたびに結果(関数積算)
  • を生成
  • identify-アキュムレータと同じ初期値を持つ(Streamが空の場合は初期値を返す)
  • combiner-パラレルStreamの各Streamの計算結果をマージ
  • // 1개 (accumulator) 
    Optional<T> reduce(BinaryOperator<T> accumulator);
    
    // 2개 (identity)
    T reduce(T identity, BinaryOperator<T> accumulator);
    
    // 3개 (combiner)
    <U> U reduce(U identity,
      BiFunction<U, ? super T, U> accumulator,
      BinaryOperator<U> combiner);
    // accumulator 예시
    OptionalInt reduced = IntStream.range(1, 4) // [1, 2, 3]
      	.reduce((a, b) -> {
        	return Integer.sum(a, b);
     	});
    // 6 (1부터 3까지 요소들을 더해줌 - 1+2+3)
    
    
    // identify 예시
    int reduced = IntStream.range(1, 4) // [1, 2, 3]
     	.reduce(10, Integer::sum);
    // 16 (초기값 10에 1부터 3까지 요소들을 더해줌 - 10+1+2+3)
    
    
    // combiner 예시
    Integer reduced = Arrays.asList(1, 2, 3)
    	// 병렬 Stream
      	.parallelStream()
     	.reduce(10, Integer::sum, (a, b) -> {
        	System.out.println("combiner was called");
            return a + b;
        });
    // combiner was called
    // combiner was called
    // 36 
    // (accumulator가 초기값 10에 각 Stream 값을 더한 11, 12, 13 세 개의 값을 구하고 
    //combiner가 여러 쓰레드에 나눠 계산한 결과를 합져준다 - 11+12=23, 23+13=36)

    4. foreach


    Streamの要素はreduce回りに回転して加工され、foreachで結果を出力する際によく使用されます.
    List<Integer> evenNumber = IntStream.range(1, 10).boxed()
    									.filter(n -> n % 2 == 0)
    									.forEach(System.out::println);
    // 2 / 4 / 6 / 8