stream(), parallelStream(), spliterator()
RichIterabe<T> extends InternalIterable<T>
大雑把にいうとIterable に色々メソッドを足してる
MutableCollection<T> extends Collection<T>, RichIterable<T>
Collection を継承してるので標準のコンテナの単なる拡張として使用できる
ImmutableCollection<T> extends RichIterable<T>
org.eclipse.collections.impl.factory の下
Lists, Sets, Maps, Bags, etc...
e.g.) Lists.immutable => ImmutableList
of / with で要素を放り込む
e.g.) Lists.immutable.of("apple", "orange", "banana")
ofAll / withAll にコンテナを与えると変換してくれる
e.g.) Lists.mutable.ofAll(Arrays.asList(1, 2, 3, 4))
// of/with (どっちも同じ)
ImmutableList<Integer> list
= Lists.immutable.of(1, 2, 3, 4); // => [1, 2, 3, 4]
MutableSet<String> set
= Sets.mutable("a", "b", "c", "b", "e");
// => ("a", "b", "c", "e")
FixedSizeMap<String, Integer> map
= Maps.fixedSize.of("one", 1, "two", 2, "three", 3);
// => {"one": 1, "two": 2, "three": 3}
// ofAll/withAll
ImmutableMap<String, Integer> map2
= Maps.immutable.ofAll(someStringIntegerMap);
// note: Map 系は ImmutableMap しか ofAll/withAll を持たない
MutableList<Foo> list2
= Lists.mutable.ofAll(fooIterable);
// note: List 系の ofAll/withAll は Iterable ならなんでもOK
コンテナのそれぞれの要素に対して、引数に与えた関数を適用して要素を変化させる
[🙂, 😉, 😛, 🙁]
| | | |
{ collect: 🙂 -> :) }
| | | |
v v v v
[:), ;), :p, :(]
コンテナのそれぞれの要素に対して、引数に与えた boolean を返す関数を適用して、true となる要素のみを含めたコンテナを返す
[🙂, 😉, 😤, 🙁]
| | | |
{ select: 🙂 -> smiling? }
| | x x
v v
[🙂, 😉 ]
コンテナのそれぞれの要素に対して、引数に与えた boolean を返す関数を適用して、false となる要素のみを含めたコンテナを返す (select の逆)
[🙂, 😉, 😤, 🙁]
| | | |
{ reject: 🙂 -> smiling? }
x x | |
v v
[ 😤, 🙁]
// collect
// Eclipse Collections
Lists.mutable.of(1, 2, 3, 4)
.collect(n -> n + 1); // => [2, 3, 4, 5]
// Stream API で対応するもの: Stream::map
Arrays.asList(1, 2, 3, 4).stream()
.map(n -> n + 1)
.collect(Collectors.toList());
// select
// Eclipse Collections
lists.mutable.of(1, 2, 3, 4)
.select(n -> n % 2 == 0); // => [2, 4]
// Stream API で対応するもの: Stream::filter
Arrays.asList(1, 2, 3, 4).stream()
.filter(n -> n % 2 == 0)
.collect.(Collectors.toList());
// reject
lists.mutable.of(1, 2, 3, 4)
.reject(n -> n % 2 == 0); // => [1, 3]
[🌶, 🥕, 🥔, 🌰, 🍖, 🍚]
| | | | | |
{ injectInto: (🍽, (a, e) -> a + 🔪(e)) }
|
v
🍛
// Eclipse Collections
Lists.immutable.of(1,2,3,4)
.injectInto(0, (a, b) -> Integer.sum(a, b)); // => 10
// Stream API で対応するもの: Stream::reduce
// reduce() の返却値は Optional<T>
Arrays.asList(1,2,3,4).stream()
.reduce((a, b) -> a + b).get();
// Eclipse Collections も 8.0 から
// Stream API のと同じ挙動の reduce() が実装された
Lists.immutable.of(1,2,3,4)
.reduce((a, b) -> Integer.sum(a, b)).get();
// allSatisfy: コンテナが持つ要素全てが引数に与えた条件を満たす場合 true を返す
Lists.mutable.of("Java", "Scala", "Clojure", "Groovy")
.allSatisfy(lang -> runsOnJVM(lang)); // => true
// anySatisfy: コンテナが持つ要素のどれか1つでも
// 引数に与えた条件を満たす場合 true を返す
Lists.mutable.of("Io", "Erlang", "Eta", "Golang")
.anySatisfy(lang -> runsOnJVM(lang)); // => true
// getFirst: コンテナが持つ最初の要素を返す
Lists.mutable.of(1, 2, 3, 4).getFirst(); // => 1
// detect: コンテナが持つ要素の内、引数に与えた条件を満たす最初のものを返す
// select().getFirst() と同じ
Lists.mutable.of(1, 2, 3, 4).detect(n -> n % == 0); // => 2
// などなど...
引数に与えた関数を適用する、というところは collect と同じ
[🙂, 😉, 😛, 🙁]
| | | |
{ flatCollect: 🙂 -> [...] }
| | | |
v v v v
[[🙂, 😀, 😇], [😉, 😜], [😛, 🤑], [🙁, 😠, 😡]]
| | | |
v v v v
[🙂, 😀, 😇, 😉, 😜, 😛, 🤑, 🙁, 😠, 😡]
# 雑なデータ構成の説明
[Series]
-(1..n)-[Title]
-(1..n)-[Episode]
↑ 再生時間持ってる
public class Title {
// ...
public ImmutableList<Episode> getEpisodes() {
return episodes;
}
}
public class Episode {
// ...
public long getDuration() {
return duration;
}
}
// collect だけで総再生時間を求めようとすると...
// ImmutableList<Title> titles;
titles.collect(Title::getEpisodes) // ImmutableList<ImmutableList<Episode>>
.collect(episodes -> episodes
.collect(Episode::getDuration)
.injectInto(0, Long::sum)) // ImmutableLongList
.injectInto(0, Long::sum); // long
とても辛い実装ですね?
※ <class>::<method> はメソッド参照という怠惰な人間のための記法 Tittle::getEpisodes だったら (title -> title.getEpisodes()) と同じ意味になる
// flatCollect で総再生時間を求めようとすると...
// ImmutableList<Title> titles;
titles.flatCollect(Title::getEpisodes) // ImmutableList<Episode>
.collect(Episode::getDuration) // ImmutableLongList
.injectInto(0, Long::sum); // long
// Stream API では flatMap
// こっちの flatMap は Stream に合わせるので以下のようになる
titles.stream()
.flatMap(title -> title.getEpisodes().stream())
.map(Episode::getDuration)
.reduce(0, Long::sum); // reduce() も初期値を受けるやつがある
flatCollect(Title::getEpisodes) のところで ImmutableList の入れ子が取れてその後の記述がスッキリした
aggregateBy(Function<? super T,? extends K> groupBy,
Function0<? extends V> zeroValueFactory,
Function2<? super V,? super T,? extends V> nonMutatingAggregator);
aggregateInPlaceBy(Function<? super T,? extends K> groupBy,
Function0<? extends V> zeroValueFactory,
Procedure2<? super V,? super T> mutatingAggregator);
// Function<T, V> は T型の値を引数にV型の値を返す関数インターフェイス
// Function0<R> は引数なしにR型の値を返す関数インターフェイス
// Function2<T1, T2, R> はT1, T2型の値を引数にR型の値を返す関数インターフェイス
// Procedure2<T1, T2> は T1, T2型の値を引数にとる返り値なしの関数インターフェイス
// aggregateBy
Lists.immutable.of(1, 2, 3, 4, 5, 6, 7)
.aggregateBy(
n -> (n % 2 == 0) ? "even" : "odd",
() -> Lists.mutable.empty(),
(accum, n) -> {
accum.add(n);
return accum;
}
);
// => {"even": [2, 4, 6], "odd": [1, 3, 5, 7]}
// aggregateInPlaceBy
Lists.immutable.of(1, 2, 3, 4, 5, 6, 7)
.aggregateBy(
n -> (n % 2 == 0) ? "even" : "odd",
() -> Lists.mutable.empty(),
(accum, n) -> {
accum.add(n); // <= return しないで accum を変更する
}
);
// 操作メソッドの前に asLazy() を挟むと
// LazyIterable に変換されて遅延評価になる
Lists.mutable.of("a", "bb", "ccc", "dd", "e")
.asLazy() // <= ここに追加する
.collect(s -> s + s)
.select(s -> s.length() > 5)
.collect(s -> s.length())
.getFirst(); // <= 最終的に1つだけあれば良いので
// select() で残る1つ目の要素までが
// 操作の適用対象となる
detect() は毎度 select(...).getFirst() を省略できる
partition() は条件関数を受けて PartitionIterable<T> を返すメソッド
PartitionIterable は条件に対する selected / rejected だった要素それぞれのコンテナを持っていて、 getSelected() / getRejected() でそれぞれを取得できる
(続き) PartitionIterable (partition()) を使う動機
select() / reject() の結果を一気に出して両方使いたい
でも forEach() の中でif文使ってどーのこーのするのは嫌
欲を言うと collectSelected/Rejected とか取り出す前に処理を重ねられる Factory かAPI が欲しい、かも
Lists.mutable.of(1,2,3,4,5,6)
.partition(n -> n % 2 == 0)
.collectSelected(n -> doSomethingForSelected(n))
.collectRejected(n -> doSomethingForRejected(n)) // みたいな
// .separate() とかここにあるような Factory を経由して結果を取得するのも良い
// ただこれをやり始めるときりがなくなるしAPIデザインとして微妙なところ
boolean anyPeopleHaveCats =
people.anySatisfy(person -> person.hasPet(PetType.CAT));
// 上の Lambda 式のが下のようになる
boolean anyPeopleHaveCats =
people.anySatisfyWith(Person::hasPet, PetType.CAT);
// 上のコードを Lambda に直すと
boolean anyPeopleHaveCats =
people.anySatisfyWith((person, pet) -> person.hasPet(pet),
PetType.CAT);
map/reduce/filter vs collect/injectInto/select
Stream APIにもcollect ってあって違う動きするから余計に...
of() で要素としてコンテナを突っ込んで flatCollect() すればできるけどそういうことではない