「中間操作」だけをまとめる。

Streamの操作には「中間操作」と「終端操作」があり、
ここでは「中間操作」だけをまとめる。

一覧表にすると次のようになる。

中間操作 概要 集合論の対応
filter フィルタリング。 select
map 型変換。 mapping
peek 副作用をさせる。
flatMap 1対多における変換。
distinct 重複排除。
sorted 並べ替え。
skip n個飛ばす。
limit n個切り取る。

中間操作でなんといっても気をつけなくてはいけないのは、
中間操作メソッドを呼び出しただけでは、
Streamオブジェクトを作っているだけということ。

中間操作メソッドに渡した処理は、
終端操作が呼び出されたときに実行される。

これを忘れてしまうと、
「全然うごかねー」ってことになりハマる。

中間操作の中でも「filter」「map」「peek」が基本で
必ず押さえておきたい。

残りは使えると便利といったところ。

filter

Stream<T> filter(Predicate<? super T> predicate)

要素のフィルタリング(select)を行う。

引数Predicateの評価がtrueになったものが
次のストリームに生き残る。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
Predicate<Integer> predicate = i -> i > 4;
List<Integer> actual = numbers.stream().filter(predicate).collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(5, 6, 7, 8);
assertThat(actual, is(expected));

map

<R> Stream<R> map(Function<? super T,? extends R> mapper)

型変換。

引数Functionにより何かの型から何かの型に変換され、
次のストリームの要素になる。

引数や戻り値のプリミティブ型対応のため、
次の派生されたメソッドが用意されている。
「mapToInt」「mapToLong」「mapToDouble」

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
Function<Integer, String> mapper = number -> number.toString();
List<String> actual = numbers.stream().map(mapper).collect(Collectors.toList());
 
List<String> expected = Arrays.asList("1", "2", "3", "4");
assertThat(actual, is(expected));

peek

Stream<T> peek(Consumer<? super T> action)

要素に対して、オブジェクトのプロパティを変更するなどの
副作用をさせる場合に用いる。

例えばfileterやmapなどの処理途中で
オブジェクトのプロパティを変更してもコンパイルエラーにならないが、
通常やってはいけない。

そういった副作用を発生させる場合は、
peekかforEachを用いる。

List<AtomicInteger> numbers = Arrays.asList(new AtomicInteger(1), new AtomicInteger(2), new AtomicInteger(3), new AtomicInteger(4));
Consumer<AtomicInteger> action = number -> number.incrementAndGet();
List<AtomicInteger> incrementedNumbers = numbers.stream().peek(action).collect(Collectors.toList());
 
//比較するために変換
List<Integer> actual = incrementedNumbers.stream().map(AtomicInteger::intValue).collect(Collectors.toList());
List<Integer> expected = Arrays.asList(2, 3, 4, 5);
assertThat(actual, is(expected));

flatMap

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

1対多における変換。

Streamを返すFunctionを
引数にしているのが特徴。

mapは1対1の変換になるが
flatMapは1対多の変換が可能になる。

使用例としては、ストリーム内の要素(オブジェクト)が保持している集合を
集める場合などに用いる。

この場合、1つの要素から1つの要素になるのではなく、
1つの要素から複数の要素になるので、mapでは対応できない。
(むりやりmapでやろうとすると大変なことになる)

flatMapでは、1つの要素からの複数の要素(オブジェクト)をストリームで返すことで、
それを連結して一つのストリームに変換する。

List<List<Integer>> numbersList = Arrays.asList(
    Arrays.asList(1, 2, 3, 4),
    Arrays.asList(5, 6),
    Arrays.asList(7, 8)
);
Function<List<Integer>, Stream<Integer>> mapper = numbers -> numbers.stream();
List<Integer> actual = numbersList.stream().flatMap(mapper).collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
assertThat(actual, is(expected));

distinct

Stream<T> distinct()

重複排除。

Object.equals(obj)がtrueの要素を
排除する。

List<Integer> numbers = Arrays.asList(1, 1, 7, 4, 7, 7, 3, 8);
List<Integer> actual = numbers.stream().distinct().collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(1, 7, 4, 3, 8);
assertThat(actual, is(expected));

sorted

Stream<T> sorted(Comparator<? super T> comparator)

並べ替え。

comparatorを指定しないで、
comparableに任せる引数なしのsorted()も存在する。

List<Integer> numbers = Arrays.asList(1, 2, 7, 4, 5, 6, 3, 8);
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
List<Integer> actual = numbers.stream().sorted(comparator).collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
assertThat(actual, is(expected));

skip

Stream<T> skip(long n)

n個飛ばす。

streamのサイズより長いnを指定しても空になるだけだが、
マイナスを指定すると例外。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> actual = numbers.stream().skip(-1).collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(3, 4);
assertThat(actual, is(expected));

limit

Stream<T> limit(long maxSize)

n個切り取る。(限定する)

streamのサイズより長いnを指定してもすべて切り取るだけだが、
マイナスを指定すると例外。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> actual = numbers.stream().limit(2).collect(Collectors.toList());
 
List<Integer> expected = Arrays.asList(1, 2);
assertThat(actual, is(expected));

PR