現場で良く使う Java Stream イディオム

f:id:Naotsugu:20191027210540p:plain

開発の現場でよく使う 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(l -> l.stream())
    .collect(Collectors.toList()); // List<String>


リストからマップへ変換

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)));


任意キーでグルーピング

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(", "));


オブジェクト型の合計

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)));