- キャスト
- null 除外
- 否定フィルタ
- Streamから配列へ
- 配列からStreamへ
- リストの集約
- リストからマップへ変換
- コレクションの変更
- 任意キーでグルーピング
- 任意キーでソート
- カンマ区切り
- プリミティブRangeからリストへ
- オブジェクト型の合計
- BigDecimal の合計
- グルーピングして集計
- グルーピングしてカウント
- 最大値/最小値の抽出
- Optional から値の取り出し
開発の現場でよく使う Java Stream イディオムです。
キャスト
List<String> studentNames = people.stream() .filter(Student.class::isInstance) .map(Student.class::cast) .map(Student::getName) .collect(Collectors.toList());
null 除外
List<String> names = people.stream() .map(People::getName) .filter(Objects::nonNull) .collect(Collectors.toList());
否定フィルタ
Java11~
int count = strings.stream()
.filter(Predicate.not(String::isEmpty)).count();
それ以前
int count = strings.stream() .filter(s -> !s.isEmpty())).count();
Streamから配列へ
stream.toArray(T[]::new);
プリミティブな Stream(IntStream など)の場合は以下
IntStream.rangeClosed(0, 3).toArray(); // [0, 1, 2, 3]
配列からStreamへ
String[] array = //...
Stream.of(array)
プリミティブ配列の場合は以下
int[] array = //... Arrays.stream(array)
リストの集約
List<List<String>> list = new ArrayList<>(); list.stream() .flatMap(Collection::stream) .collect(Collectors.toList()); // List<String>
配列の場合は以下
List<String[]> list = List.of("ab,cd,ef".split(","), "12,34,56".split(",")); List<String> result = list.stream() .flatMap(Stream::of) .collect(Collectors.toList()); // [ab,cd,ef,12,34,56]
リストからマップへ変換
list.stream().collect( Collectors.toMap(Item::getId, UnaryOperator.identity()));
キー重複がある場合は IllegalStateException
となるため以下のようにする必要がある(以下は後勝ちの例)
list.stream().collect( Collectors.toMap(Item::getId, UnaryOperator.identity(), (e1, e2) -> e2));
値に null
が有る場合、Collectors.toMap()
は NullPointerException
となるため以下のようにする必要がある
list.stream().collect(HashMap::new, (Map m, Item i) -> m.put(i.getId(), i.getValue()), Map::putAll);
並びを維持した LinkedHashMap への変換
list.stream().collect( Collectors.toMap( Item::getId, UnaryOperator.identity(), (e1, e2) -> e1, LinkedHashMap::new)));
コレクションの変更
Set<String> set = list.stream()
.collect(Collectors.toCollection(LinkedHashSet::new));
任意キーでグルーピング
Map<String, List<Item>> m = items.stream() .collect(Collectors.groupingBy(Item::getCode));
任意キーでソート
list.stream() .sorted(Comparator.comparing(Item::getId)) .collect(Collectors.toList());
複合キーのソート
list.stream() .sorted(Comparator.comparing(Item::getId) .thenComparing(Item::getName)) .collect(Collectors.toList());
ソートキーに null
を含む場合
list.stream() .sorted(Comparator.comparing(Item::getName, Comparator.nullsFirst(Comparator.naturalOrder()))) .collect(Collectors.toList());
任意順序でのソート
List<String> priorities = Arrays.asList("top", "middle", "bottom"); List<Item> sorted = list.stream() .sorted(Comparator.comparing( (Item item) -> priorities.indexOf(item.getName()))) .collect(Collectors.toList());
カンマ区切り
numbers.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
プリミティブRangeからリストへ
IntStream.rangeClosed(10, 20).boxed().collect(Collectors.toList());
オブジェクト型の合計
int sum = integers.stream()
.mapToInt(Integer::intValue)
.sum();
Integer sum = integers.stream()
.reduce(0, Integer::sum);
BigDecimal の合計
BigDecimal sum = numbers.stream() .reduce(BigDecimal.ZERO, BigDecimal::add);
グルーピングして集計
Map<LocalDate, BigDecimal> m = list.stream().collect(
Collectors.groupingBy(
Item::getSalesDate,
Collectors.reducing(BigDecimal.ZERO, Item::getPrice, BigDecimal::add)));
なお、reducing
の第一引数である初期値は、stream処理全体で1回取得したものが流用される。そのため BigDecimal::add
のように、結果として新しいオブジェクトを生成するメソッドを使わなければならない(ミュータブルな操作を行った場合、異なるグルーピング間で初期インスタンスが共有されてしまいおかしなことになる)。
グルーピングしてカウント
Map<LocalDate, Long> count = list.stream().collect(
Collectors.groupingBy(
Item::getSalesDate,
Collectors.counting()));
最大値/最小値の抽出
List<LocalDate> dates = Arrays.asList( LocalDate.of(2023, 1, 2), LocalDate.of(2023, 1, 1), null, LocalDate.of(2023, 1, 4), LocalDate.of(2023, 1, 3)); LocalDate min = dates.stream().min(Comparator.nullsLast(Comparator.naturalOrder())) .orElseThrow(NoSuchElementException::new); // 2023-01-01
Comparator.nullsFirst(Comparator.naturalOrder())
とした場合は NullPointerException
となる。
オブジェクトのフィールドでソートする場合には以下のようになる。
.min(Comparator.comparing(Person::getAge))
Optional から値の取り出し
Optional::isPresent
である中身を抽出。
List<Optional<String>> optionals = Arrays.asList( Optional.of("foo"), Optional.empty(), Optional.of("bar"));
Java9~
List<String> names = optionals.stream() .flatMap(Optional::stream) .collect(Collectors.toList());
それ以前
List<String> names = optionals.stream() .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) .collect(Collectors.toList());