JavaのStream APIとSQLの対比に関するメモ。
Streamの公式ドキュメント
Java8の公式ドキュメントの内容を抜粋。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html
順次および並列の集約操作をサポートする要素のシーケンスです。
(中略)
コレクションとストリームは、表面上似た点があるものの、異なる目的を持っています。コレクションは主に、要素の効率的な管理やアクセスに注力しています。これに対し、ストリームは要素の直接アクセスや操作の手段を持たず、代わりにソースやそのソースに対して集約的に実行される計算操作を宣言的に記述することに注力しています。
(中略)
ストリーム・パイプラインは順次、並列のいずれかで実行できます。この実行モードは、ストリームのプロパティの1つです。ストリームの作成時には、順次実行または並列実行の初期選択が行われます。
僕の理解が足りなくてドキュメント読んでも分かりにくいですが、ざっくり書くとこんなもの。
- コレクションと似ている
- 集約操作を行う
- 要素の直接アクセスや操作(get, remove)をするものではない
- 処理を順次・並列で選択できる
集約操作はSQLをイメージしてもらえれば理解は速くなります。
Stream APIとSQLの対比一覧
Stream API | 対応するSQL | 説明 |
---|---|---|
Stream#map | SELECT | Stream内の要素を別の値やオブジェクトに変換する操作 |
Stream#filter | WHERE | 条件に一致したデータのみにフィルタリングする操作。引数にはbooleanが戻り値となる式を書くことが必要。 |
Stream#max | MAX | 最大値の取得 |
Stream#min | MIN | 最小値の取得 |
IntStream#sum Collectors#summingInt |
SUM | 合計の取得 |
サンプルコード
前提条件
- lombok プラグインが導入されている前提
- スコープは適当
人物データとクラス
人物データを格納するテーブルとそのデータを保持するクラスを以下のように定義する。
■Personテーブル
id | name | age | job |
---|---|---|---|
1 | yamada | 25 | ---: |
2 | suzuki | 28 | ---: |
3 | sato | 30 | ---: |
4 | nakamura | 41 | ---: |
5 | yamamoto | 38 | ---: |
6 | akiyama | 22 | ---: |
7 | tanabe | 43 | ---: |
8 | ito | 24 | ---: |
■Personクラス
@Data
@AllArgsConstructor
public class Person {
private int id;
private String name;
private int age;
}
更にPersonテーブルの全データを格納したリストをList<Person>
として定義する。
List<Person>
は以下と同義である。
List<Person> personList = Arrays.asList(
new Person(1, "yamada", 25),
new Person(2, "suzuki", 28),
new Person(3, "sato", 30))
new Person(4, "nakamura", 41))
new Person(5, "yamamoto", 38))
new Person(6, "akiyama", 22))
new Person(7, "tanabe", 43))
new Person(8, "ito", 24))
);
Stream#map
List<String> nameList = personList.stream()
.map(Person::getName)
.collect(Collectors.toList());
SELECT
p.name
FROM
Person p
<結果>
["yamada", "suzuki", "sato", "nakamura, "yamamoto", "akiyama", "tanabe", ito]
Stream#filter
List<String> nameList = personList.stream()
.filter(p -> p.getAge() >= 40)
.map(Person::getName)
.collect(Collectors.toList());
SELECT
p.name
FROM
Person p
WHERE
p.age >= 40
<結果>
["nakamura", "tanabe"]
Stream#max
Integer max = personList.stream()
.map(Person::getAge)
.max(Comparator.comparingInt(x -> x))
.orElse(null);
SELECT
max(p.age) max_age
FROM
Person p
<結果>
43
Stream#min
Integer max = personList.stream()
.map(Person::getAge)
.min(Comparator.comparingInt(x -> x))
.orElse(null);
SELECT
min(p.age) min_age
FROM
Person p
<結果>
22
IntStream#sum/Collectors#summingInt
int sum = personList.stream()
.mapToInt(Persion::getAge)
.sum();
int sum2 = personList.stream()
.collect(Collectors.summingInt(Persion::getAge));
SELECT
sum(p.age) sum_age
FROM
Person p
<結果>
251