TOP > カテゴリ > Java・Android >

Javaのラムダ式とStream API

Javaの使い方(目次)

Java8から導入された「ラムダ式」と「Stream API」の基本パターンです。1時間ぐらいでモダンな機能の基本をマスターできると思います。

目次

1. ラムダ式(LAMBDA)
2. Stream API(ラムダ式の活用)
  2-1. List版
  2-2. Set版
  2-3. Map版
  2-4. 配列をStreamに変換する
  2-5. 並列処理
3. Stream APIの例
  3-1. filter(フィルタリング/絞込み)
  3-2. map(マッピング/値の編集)
  3-3. allMatch,anyMatch,noneMatch(マッチング/真偽)
  3-4. sorted(ソート/整列)
  3-5. count(カウント/リスト数)
  3-6. average(アベレージ/平均)
  3-7. max(マックス/最大値)
  3-8. min(ミニ/最小値)
  3-9. limit(リミット/要素数の制限)
  3-10. distinct(ディスティンクト/重複削除)
  3-11. flatMap(フラットマップ/新リストの作成)
  3-12. reduce(リダクション/要素を集計する)
  3-13. collect(コレクト/オブジェクトを返す)
  3-14. groupingBy(グルーピング/要素の分類)
  3-15. まとめ

1. ラムダ式(LAMBDA)

ラムダ式では関数オブジェクトを即時利用(定義及び生成)できます。

// メソッドの型を作成する(インターフェース)
interface MyFunction {
    int run(int x);
}

public class Main {

   public static void main(String[] args) {;
        
       // ラムダ式でメソッドの参照(ポインタ)を取得する
       MyFunction my = (int x) -> {return x * 10;};
       
       // メソッドを実行する
       System.out.println(my.run(1));
   
       // ラムダ式でメソッドを即時実行する
       int val = ((MyFunction)((int x) -> {return x * 10;})).run(1);
       System.out.println(val);       
   }    
}

結果

10
10

コードの省略

ラムダ式はコードを次のように省略可能なのでスッキリします。

引数の型を省略できる。(型推論)
引数が1つの場合は()を省略できる。
{}が単文の場合は{}とreturnを省略できる。

// メソッドの型を作成する(インターフェース)
interface MyFunction {
    int run(int x);
}

public class Main {

   public static void main(String[] args) {;
        
       // 元のラムダ式
       MyFunction my1 = (int x) -> {return x * 10;};
       System.out.println(my1.run(1));
       
       // 型の省略
       MyFunction my2 = (x) -> {return x * 10;};
       System.out.println(my2.run(1));
       
       // ()の省略
       MyFunction my3 = x -> {return x * 10;};
       System.out.println(my3.run(1));
       
       // {}とreturnの省略
       MyFunction my4 = x -> x * 10;
       System.out.println(my4.run(1)); 
   }    
}

結果

10
10
10
10

2. Stream API(ラムダ式の活用)

コレクションにStream APIとラムダ式を使用する事が可能です。

2-1. List版

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

public class Main {

   public static void main(String[] args) {;

       // リストの生成
       List<Integer> list = Arrays.asList(1,2,3);

       // 拡張for文
       for(Integer x : list) {
           System.out.println(x);
       }

       System.out.println("---");
       
       // Stream APIとラムダ式を活用する
       list.stream().forEach(x -> System.out.println(x));
       
       System.out.println("---");
       
       // 引数の型/個数が一致している場合は更に省略可能
       list.stream().forEach(System.out::println);       
   }
}

結果

1
2
3
---
1
2
3
---
1
2
3

2-2. Set版

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Main {

   public static void main(String[] args) {;

       // リストの生成
       Set<String> list = new HashSet<>(Arrays.asList("あ","い"));
       
       // 拡張for文
       for(String x : list) {
           System.out.println(x);
       }

       System.out.println("---");
       
       // Stream APIとラムダ式を活用する
       list.stream().forEach(x -> System.out.println(x));
       
       System.out.println("---");
       
       // 引数の型/個数が一致している場合は省略可能
       list.stream().forEach(System.out::println);       
   }
}

結果



---


---

2-3. Map版

import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {;

        Map<String,String> map = new HashMap<>();
        
        map.put("R" ,"赤色");
        map.put("G" ,"緑色");
        map.put("B" ,"青色");
        
        // 拡張for文
        for(String key : map.keySet()) {
            System.out.println(key + ":" + map.get(key));
        }       
        
        System.out.println("---");
        
        // Stream APIとラムダ式を活用する
        map.entrySet().stream()
          .map(v -> v.getKey() + ":" + v.getValue())
          .forEach(x -> System.out.println(x));        
        
        System.out.println("---");
        
        // 引数の型/個数が一致している場合は省略可能
        map.entrySet().stream()
          .map(v -> v.getKey() + ":" + v.getValue())
          .forEach(System.out::println);
   }
}

結果 ※Mapは環境により順序が異なる

R:赤色
B:青色
G:緑色
---
R:赤色
B:青色
G:青色
---
R:赤色
B:青色
G:緑色

2-4. 配列をStreamに変換する

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

public class Main {
  public static void main(String[] args) {;

      Integer[] list  = {1,2,3};
      
      Stream<Integer> st = Arrays.stream(list);
      
      st.forEach(System.out::println);
  }
}

結果

1
2
3

2-5. 並列処理

stream()の代わりにparallelStream()を使用すると並列処理を行います。環境によっては処理速度が速くなる場合があります。

3. Stream APIの例

次節からStreamの基本的なパターンをご紹介します。

公式APIの解説Stream
Comparator
Collectors

不明点は公式のAPI解説書をご覧下さい。

3-1. filter(フィルタリング/絞込み)

SQLで言うとWHEREと同様です。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3);
     
      // filter
      list.stream()
            .filter(i -> i <= 2)
            .forEach(System.out::println);
  }
}

結果

1
2

3-2. map(マッピング/値の編集)

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

public class Main {
  public static void main(String[] args) {;

      List<String> list = Arrays.asList("A","B","C");
     
      // map
      list.stream()
            .map(s -> s.toLowerCase())
            .forEach(System.out::println);
  }
}

結果

a
b
c

3-3. allMatch,anyMatch,noneMatch(マッチング/真偽)

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

public class Main {
  
    public static void main(String[] args) {;
        
        // リストの生成
        List<String> list = Arrays.asList("A","B","A");
        
        // allMatch(全て一致する)
        boolean flg1 = list.stream()
                           .allMatch("A"::equals);              
        System.out.println(flg1);
        
        // anyMatch(いずれかが一致する)
        boolean flg2 = list.stream()
                           .anyMatch("A"::equals);              
        System.out.println(flg2);        
       
        // noneMatch(全て一致しない)
        boolean flg3 = list.stream()
                           .noneMatch(a -> (a == "C"));              
        System.out.println(flg3);          
    }
}

結果

false
true
true

3-4. sorted(ソート/整列)

SQLのORDER BYと同様。

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

public class Main {

    public static void main(String[] args) {;

        List<Integer> list = Arrays.asList(3,1,2);

        // sorted(昇順)
        list.stream()
          .sorted(Comparator.naturalOrder())
          .forEach(System.out::println);
        
        System.out.println("---");
        
        // sorted(降順)
        list.stream()
        .sorted(Comparator.reverseOrder())
        .forEach(System.out::println);
   }
}

結果

1
2
3
---
3
2
1

3-5. count(カウント/リスト数)

SQLのCOUNTと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3);
     
      // count
      long val = list.stream().count();
      
      System.out.println(val);
  }
}

結果

3

3-6. average(アベレージ/平均)

SQLのAVGと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3);
     
      // average
      double val = list.stream()
                           .mapToInt(x->x)
                           .average()
                           .getAsDouble();
      
      System.out.println(val);
  }
}

結果

2.0

3-7. max(マックス/最大値)

SQLのMAXと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3);
     
      // max
      Integer val = list.stream()
                          .max(Comparator.naturalOrder())
                          .get();      
      
      System.out.println(val);
  }
}

結果

3

3-8. min(ミニ/最小値)

SQLのMINと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3);
     
      // min
      Integer val = list.stream()
                          .min(Comparator.naturalOrder())
                          .get();      
      
      System.out.println(val);
  }
}

結果

1

3-9. limit(リミット/要素数の制限)

SQLのLIMITと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,2,3,4,5);
     
      // limit
      list.stream()
            .limit(3)
            .forEach(System.out::println);
  }
}

結果

1
2
3

3-10. distinct(ディスティンクト/重複削除)

SQLのDISTINCTと同様。

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

public class Main {
  public static void main(String[] args) {;

      List<Integer> list = Arrays.asList(1,3,3,3,5);
     
      // distinct
      list.stream()
            .distinct()
            .forEach(System.out::println);
  }
} 

結果

1
3
5

3-11. flatMap(フラットマップ/新リストの作成)

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

public class Main {

    public static void main(String[] args) {;

        List<String> list = Arrays.asList("1,2,3","4,5,6");
        
        // flatMap
        list.stream()
               .flatMap(s -> Arrays.stream(s.split(",")))
               .forEach(System.out::println);

   }
}

結果

1
2
3
4
5
6

3-12. reduce(リダクション/要素を集計する)

要素を集計して集計結果を返します。SQLのSUMに近いです。

<例1>

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

public class Main {

    public static void main(String[] args) {;

        List<Integer> list = Arrays.asList(1,2,3,4,5);

        // reduce
        Integer sum = list.stream()
                            .reduce(Integer::sum)
                            .get();

        System.out.println(sum);
   }
}

結果

15

<例2>

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

public class Main {

    public static void main(String[] args) {;

        List<Integer> list = Arrays.asList(1,2,3,4,5);

        // reduce
        Integer sum = list.stream()
                            .reduce((a,b) -> a + b)
                            .get();

        System.out.println(sum);
   }
}

結果

15

<例3>

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

public class Main {

    public static void main(String[] args) {;

        List<Integer> list = Arrays.asList(1,2,3,4,5);

        // reduce
        Integer sum = list.stream()
                            .reduce(10,(a,b) -> a + b);

        System.out.println(sum);
   }
}

結果

25

※stream()とparallelStream()では計算結果が異なるので注意して下さい。

3-13. collect(コレクト/オブジェクトを返す)

オブジェクトは次のストリームがある場合は「次のストリームへ」。ない場合は「戻り値」になります。※次節の3-14もご覧下さい。

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

public class Main {

    public static void main(String[] args) {;

        List<Integer> list = Arrays.asList(1,2,3);

        // collect
        List<Integer> result = list.stream().collect(Collectors.toList());
        result.forEach(System.out::println);
   }
}

結果

1
2
3

3-14. groupingBy(グルーピング/要素の分類)

SQLのGROUP BYと同様。

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

public class Main {
  
    public static void main(String[] args) {;

        List<String> list = Arrays.asList
                              ("とら","たらんちゅあ","ねこ","しまうま","きりん");
     
        // groupingBy
        list.stream()
           .collect(Collectors.groupingBy(s -> s.length()))
           .forEach((a,b) -> System.out.println(a + ":" + b));      
  }
}

結果

2:[とら, ねこ]
3:[きりん]
4:[しまうま]
6:[たらんちゅあ]

3-15. まとめ

Stream APIはデータをSQLのように操作できる仕組みです。

また、mapやfilterなどはメソッドチェーン(メソッドを連続して呼び出す)で複数、組み合わせて使用する事が可能です。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
public class Main {
  
    public static void main(String[] args) {;
 
        List<String> list = Arrays.asList("とら","しまうま","きりん");
     
        list.stream()
           .map(a->a + "A")
           .filter(a-> a.length() != 0)
           .filter(a-> a.length() < 5)
           .collect(Collectors.groupingBy(s -> s.length()))
           .forEach((a,b) -> System.out.println(a + ":" + b));      
  }
}

結果

3:[とらA]
4:[きりんA]

初めての方には「やや難しい」ですが便利なので色々と試すと良いです。





関連記事



公開日:2018年05月06日 最終更新日:2018年05月18日
記事NO:02644