- 参考
- 読み方
- filter
- forEach
- map
- List or 配列 からStream生成
- toList(StreamからListに変換)
- ラムダ式記法(省略形と非省略形)
- 実践編:値の詰替え
- 条件によって処理を変えたい時は?
- StreamAPIのメリット
職場でStreamAPIを使ったので備忘録として。
参考
入門Javaのラムダ式とStream API #Java - Qiita
【Java】 Stream(filter, map, forEach, reduce) #java8 - Qiita
読み方
// Stream APIを使用して条件に合うメンバーを抽出し、該当する場合に「たぬき」を表示 memberList4.stream() .filter(member -> "ぽんぽこ".equals(member.name())) .filter(member -> member.age() >= 1000) .forEach(member -> { System.out.println("たぬき"); });
filterがif文であり、条件をクリアしたら下に処理が移っていくと思っておけばOK。
memberList4を分解して、1個ずつ引数のmemberに渡っていくイメージ。
forEachの中にやりたい処理を書く。
filter
if文
//recordはgetter/setterやコンストラクタを自動生成してくれる便利機能
// ここではnameとageのgetter/setterが作られている
record Member(String name, int age) {
}
//ネタ元のList生成
List<Member> memberList4 = List.of(
new Member("ぽんぽこ", 1006),
new Member("ピーナッツくん", 5),
new Member("チャンチョ", 10),
new Member("コモラ", 16));
//1000歳以上で名前がぽんぽこの場合 拡張for文バージョン
for(Member member : memberList4) {
if(member.name.equals("ぽんぽこ")) {
if(member.age >= 1000) {
//何らかの処理
}
}
}
// Stream APIバージョン
memberList4.stream()
.filter(member -> "ぽんぽこ".equals(member.name()))
.filter(member -> member.age() >= 1000);
名前がぽんぽこで、年齢が1000歳以上が条件。
ここは見たままなので特に解説はなし。
forEach
繰り返し処理。
filter条件をクリアした場合だけ名前を表示したい。
//recordはgetter/setterやコンストラクタを自動生成してくれる便利 // ここではnameとageのgetter/setterが作られている record Member(String name, int age) { } //ネタ元のList生成 List<Member> memberList4 = List.of( new Member("ぽんぽこ", 1006), new Member("ピーナッツくん", 5), new Member("チャンチョ", 10), new Member("コモラ", 16)); //1000歳以上で名前がぽんぽこの場合 for文バージョン for(Member member : memberList4) { if(member.name.equals("ぽんぽこ")) { if(member.age >= 1000) { System.out.println(member.name()); } } } // Stream APIを使用して条件に合うメンバーを抽出し、該当する場合に「たぬき」を表示 memberList4.stream() .filter(member -> "ぽんぽこ".equals(member.name())) .filter(member -> member.age() >= 1000) .forEach(member -> System.out.println(member.name()));
forEachが最初ではなくて、最後にforEachが来るのがポイント。
filter条件をクリアしたものだけforEachの処理に到達する。
補足↓ 拡張for文とforEach比較。全ての名前を表示する。
//拡張for文 for(Member member : memberList4) { System.out.println(member.name()); } //forEach memberList4.forEach(member -> System.out.println(member.name()));
map
型の変換やら値の更新をしたいときに使う。
実体としては、関数型インターフェイスR apply(T t)を実装している。
つまりT型を受け取ってR型を返却さえすればよいので、かなり何でもできる。
RとTが同じ型でも良い。
//recordはgetter/setterやコンストラクタを自動生成してくれる便利 // ここではnameとageのgetter/setterが作られている record Member(String name, int age) { } //ネタ元のList生成 List<Member> memberList4 = List.of( new Member("ぽんぽこ", 1006), new Member("ピーナッツくん", 5), new Member("チャンチョ", 10), new Member("コモラ", 16)); //for文バージョン for(Member member : memberList4) { String ageString = String.valueOf(member.age());//ここがmap if(ageString.equals("5")) { System.out.println(ageString+"歳"); } } //Streamバージョン memberList4.stream() .map(member -> String.valueOf(member.age())) //ageをStringに変換 .filter(stringAge -> stringAge.equals("5")) //filterの引数にはStringに変換後のageが渡る .forEach(stringAge -> System.out.println(stringAge+"歳"));
ここでのmapはMember型を受取り、String型を返している。
関数型インターフェイスR apply(T t)をString apply(Member member)として実装していることになる。
List or 配列 からStream生成
List<Member> memberList4 = List.of(
new Member("ぽんぽこ", 1006),
new Member("ピーナッツくん", 5),
new Member("チャンチョ", 10),
new Member("コモラ", 16));
// ListからStreamを生成
Stream<Member> stream = memberList.stream();
Member[] members = {
new Member("ぽんぽこ", 1006),
new Member("ピーナッツくん", 5),
new Member("チャンチョ", 10),
new Member("コモラ", 16));
};
// 配列からStreamを生成
Stream<Member> stream = Arrays.stream(members);
toList(StreamからListに変換)
Stream<String> mappedStream = ...; // Listに変換する List<String> resultList = mappedStream.toList();
ラムダ式記法(省略形と非省略形)
JavaScriptとほとんど同じ。
//非省略形 Hoge hoge = (str) -> { return str.length(); }; //省略形 Hoge hoge = str -> str.length();
実践編:値の詰替え
よくある値の詰替処理をStreamAPIでやってみる。
//recordはgetter/setterやコンストラクタを自動生成してくれる便利 // ここではnameとageのgetter/setterが作られている record Member(String name, int age) { } record Test(String name, int age) { } //ネタ元のList生成 List<Member> memberList4 = List.of( new Member("ぽんぽこ", 1006), new Member("ピーナッツくん", 5), new Member("チャンチョ", 10), new Member("コモラ", 16)); //for文バージョン for(Member member : memberList4) { if("チャンチョ".equals(member.name())) { new Test(member.name(), member.age()); } } //StreamAPIバージョン List<Test> testList = memberList.stream() .filter(member -> member.name().equals("チャンチョ")) .map(member -> new Test(member.name(), member.age())) //Member型からTest型に詰め替え .toList();
条件によって処理を変えたい時は?
例えば20歳以上と16歳以下で処理を分けたい時。
ChatGPTに聞いてみいたらif else文を使ったコードが返ってきた。
filterを使って上手いことできないのだろうか?
import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { record Member(String name, int age) { } // 元となるList List<Member> memberList = List.of( new Member("ぽんぽこ", 1006), new Member("ピーナッツくん", 5), new Member("チャンチョ", 10), new Member("コモラ", 16)); // Streamを使用して条件に応じた出力を行う memberList.stream() .forEach(member -> { if (member.age() >= 20) { System.out.println(member.name() + ":20歳以上"); } else if (member.age() <= 16) { System.out.println(member.name() + ":16歳以下"); } else { System.out.println(member.name() + ":" + member.age() + "歳"); } }); } }
StreamAPIのメリット
読みやすい。
ネストが深くなりづらいので、直感的に読める。
ただ、forEachの中で大量の処理をすると読みづらくなりそう。