この記事ではJava Stream APIが読めない人を対象にしています。
以下の人にオススメです
- Javaとか昔は書いてたけど最近は書いてない
- Stream APIが読めなくてコーディングを辞めた
- とにかく時間がないから勘所だけ知りたい
最初に
Java Stream APIとは、 InputStreamや、OutputStreamなどのI/Oクラスとは関係ありません。
List,SetなどのCollection Object,または配列などのループ可能な要素をループの中で一度だけ処理する仕組みの事を言います。
※Streamは一度処理すると再利用できません。ただし、Streamはchain(連鎖)させることで複数の処理が可能です。
Stream APIを利用して行うものは、よく使うのは以下の2点です
- エンティティを変換する(mapメソッド)
- コレクションをフィルターする(filterメソッド)
Javaのコードを記載します。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
List<String> csvList = new ArrayList<>();
csvList.add("田中 直樹,MAN");
csvList.add("北村 一輝,MAN");
csvList.add("加藤 しずか,WOMAN");
csvList.add("小野 妹子,UNKNOWN");
// 1.エンティティを変換する。(map)
List<Person> persons = csvList.stream().map(Person::convertPersonFromCSV).collect(Collectors.toList());
// 2.エンティティをフィルターする。(filter)
List<Person> mens = persons.stream().filter(p -> p.getGender() == Gender.MAN).collect(Collectors.toList());
System.out.println("Print all persons");
persons.forEach(System.out::println);
System.out.println("Print mens only");
mens.forEach(p -> System.out.println(p.getName()));
}
enum Gender {
MAN,
WOMAN,
UNKNOWN;
}
static class Person {
private String name;
private Gender gender;
public Person(String name, Gender gender) {
this.name = name;
this.gender = gender;
}
@Override
public String toString() {
return String.format("My name is %s . \r\nMy gender is %s.", name, gender);
}
public String getName() {
return this.name;
}
public Gender getGender() {
return this.gender;
}
public static Person convertPersonFromCSV(String csv) {
String[] csvStrings = csv.split(",");
if (csvStrings.length != 2) throw new IllegalArgumentException("csv is illegal format.");
return new Person(csvStrings[0], Gender.valueOf(csvStrings[1]));
}
}
}
それでは、順番に上記を見ていきましょう
エンティティを変換する
エンティティを変換する際は、mapメソッドを使用します。
mapメソッドで変換、collectメソッドでStreamを再度Collection型に変換します。
主に以下の3種類の書き方が出来ます。
csvList.stream().map(Person::convertPersonFromCSV).collect(Collectors.toList());
csvList.stream().map(csv -> Person.convertPersonFromCSV(csv)).collect(Collectors.toList());
csvList.stream().map(csv -> {
Person person = Person.convertPersonFromCSV(csv);
return person;
}).collect(Collectors.toList());
どれもやっていることは同じで、csvListの要素であるStringを取得して、
Personオブジェクトに変換して返却するメソッドを実行しています。
- 1番目は引数のcsvを省略する書き方
- 2番目はreturnメソッドを省略して1行で記述する書き方
- 3番目は何も省略しない書き方
Streamを使うことで、Listを1行(ワンライナー)でListに変換することが出来ました。
Streamを使用することのメリットの一つとして、コードの記述量を減らすことが出来るというものがあります。
以下は時間のない人は飛ばしてください。
- csvList.stream()
- Streamオブジェクトを取得します。
- map(Person::convertPersonFromCSV)
- mapメソッドの引数として、csvListのGenericインターフェースであるStringが与えられます。
- Personクラスのエンティティ変換メソッドを呼び出しentityの変換を行い、entityクラスを返却します。
- Functionインターフェースを実装したメソッドを使用して、StringクラスをPersonクラスに変換して返却しています。
- collect(Collectors.toList())
- 変換されたPersonを集めます。
- Collectors.toList()でListクラスに追加していきます。
- Collectors.toXXX()でSetやMapにも変換できます。toMapの場合、keyとvalueを返却する2つのFunctionを記述する必要があります。
- メソッドの処理の順序としては、「mapで変換,collectでStreamで処理したオブジェクトをリストに格納」という処理を繰り返すイメージとなります。
コレクションをフィルターする
コレクションから特定の要素を除外して、新しいコレクションオブジェクトを作成することが出来ます。
filterメソッドを使用して、残したい対象のオブジェクトを記載します。
persons.stream().filter(p -> p.getGender() == Gender.MAN).collect(Collectors.toList());
Streamを使うことで、Listから男性のみを抽出した新しいコレクションをワンライナーで作成できました。
- filter(p -> p.getGender() == Gender.MAN)
- Personを引数に取り、booleanを返却します。
Stream APIを使用してよくやる他のこと
- findAny
- filterと組み合わせて、特定の要素が存在するかどうかを検出する。
- sum
- mapToIntと組み合わせて、コレクションの合計数を算出する。
- reduce
- コレクションの要素を結合して一つのオブジェクトに纏める。