少し前まで、私が取り組んでいるプロジェクトの1つをJava 8に移行することができました。 もちろん、最初は、Stream APIを使用するときの構造のコンパクトさと表現力に陶酔感がありましたが、時間が経つにつれて、より短く、より柔軟で表現力豊かに書きたいと思いました。 最初は、ユーティリティクラスに静的メソッドを追加しましたが、これによりコードが悪化しました。 最後に、ストリームインターフェイス自体を拡張する必要があるという結論に達しました。その結果、小さな
StreamExライブラリが
生まれました 。
Java 8には、オブジェクト
Streamと、3つのプリミティブな
IntStream 、
LongStream、および
DoubleStreamの 4つのストリームインターフェイスがあります。 完全に交換するには、標準スレッドですべてをラップする必要があります。 したがって、
StreamEx 、
IntStreamEx 、
LongStreamExおよび
DoubleStreamExを
DoubleStreamEx 。 元のインターフェイスを保存するには、次のような退屈なメソッドを大量に作成する必要がありました。
public class IntStreamEx implements IntStream { private final IntStream stream; @Override public <U> StreamEx<U> mapToObj(IntFunction<? extends U> mapper) { return new StreamEx<>(stream.mapToObj(mapper)); } ... }
また、静的コンストラクターを作成する必要がありました。これは、元のクラスに既にあるものだけでなく、他のものもあります(
random.ints()を置き換えるために、
IntStreamEx.of(random)メソッドがあります)。 しかし、その後、自分の裁量で拡大できるストリームがありました。 以下は、追加機能の簡単な概要です。
人気のコレクターの減少
標準のStream APIでは、多くの場合、
.collect(Collectors.toSet())または
.collect(Collectors.toList())を記述する必要があります。
Collectors静的にインポートしても、冗長に見えます。
StreamExメソッド
toSet 、
toList 、
toCollection 、
toMap 、
groupingByをいくつかの署名とともに追加しました。 これがIDである場合、キーの関数から
toMapを省略
toMapことができます。 いくつかの例:
List<User> users; public List<String> getUserNames() { return StreamEx.of(users).map(User::getName).toList(); } public Map<Role, List<User>> getUsersByRole() { return StreamEx.of(users).groupingBy(User::getRole); } public Map<String, Integer> calcStringLengths(Collection<String> strings) { return StreamEx.of(strings).toMap(String::length); }
joiningメソッドもコレクターに対応しますが、その前に、ストリームのコンテンツは
String::valueOf介して渡されます。
public String join(List<Integer> numbers) { return StreamEx.of(numbers).joining("; "); }
検索とフィルタリングを削減
ストリーム内の特定のクラスのオブジェクトのみを選択する必要がある場合があります。
.filter(obj -> obj instanceof MyClass)書くことができ
.filter(obj -> obj instanceof MyClass) 。 ただし、これではストリームのタイプが明確にならないため、要素のタイプを手動でキャストするか、別のステップ
.map(obj -> (MyClass)obj)追加する必要があります。
StreamExを使用する
StreamExこれはselectメソッドを使用して簡潔に行われます。
public List<Element> elementsOf(NodeList nodeList) { return IntStreamEx.range(0, nodeList.getLength()).mapToObj(nodeList::item).select(Element.class).toList(); }
ちなみに、
selectメソッドの実装では、マップステップは使用されませんが、フィルタリングの直後にストリームタイプの安全でない変換が適用されるため、パイプラインは再び長くなりません。
多くの場合、ストリームからnullをスローする必要があるため、
nonNull()メソッドを追加して
filter(Objects::nonNull)を置き換え
filter(Objects::nonNull) 。 また、ストリームから述語を満たす要素を
remove(Predicate)メソッドもあります(逆に
filter )。 メソッド参照をより頻繁に使用できます。
public List<String> readNonEmptyLines(Reader reader) { return StreamEx.ofLines(reader).map(String::trim).remove(String::isEmpty).toList(); }
findAny(Predicate)および
findFirst(Predicate) filter(Predicate).findAny()および
filter(Predicate).findFirst()ます。
hasメソッドを使用すると、ストリームに特定の要素があるかどうかを確認できます。 同様のメソッドがプリミティブストリームに追加されます。
追加および追加
多くの場合、1つまたは2つの特別な値をストリームに追加するか、2つのストリームを接着する必要があります。 標準の
Stream.concatを使用することは、ネストされたブラケットを追加し、プログラムを左から右に読むという考えを台無しにするため、あまり良く
Stream.concatません。
concatを置き換えるために
concat appendおよび
prependを作成しました。これにより、現在のストリームの末尾または先頭に別のストリームまたは特定の値セットを追加できます。
public List<String> getDropDownOptions() { return StreamEx.of(users).map(User::getName).prepend("(none)").toList(); }
次のように配列を展開できます。
public int[] addValue(int[] arr, int value) { return IntStreamEx.of(arr).append(value).toArray(); }
コンパレータ
Java 8では、
Comparator.comparingIntなどのキー抽出メソッドを使用してコンパレーターを作成する方がはるかに簡単です。 最も一般的な並べ替えの状況を減らすために、1つのキーの最大値と最小値を検索し、
sortingBy 、
maxBy 、および
minByメソッドのファミリーを
minBy 。
public User getMostActiveUser() { return StreamEx.of(users).maxByLong(User::getNumberOfPosts).orElse(null); }
ところで、コンパレータによる並べ替えがプリミティブストリームに追加されました(便利な場合もあります)。 確かに、ボンネットの下には余分なボクシングがありますが、JITコンパイラーの積極的な最適化に頼ることができます。
反復可能
多くの人は、
Iterable iterator()メソッドが含まれているため、
Stream Iterableインターフェイス
Stream実装することを
Iterableます。 特に、
Iterable再利用性を意味し、イテレータはストリームから1回しか取得できないため、これは行われていません。
Stack Overflowは、JDKにはすでにこの規則
DirectoryStreamの例外があることを指摘しています。 何らかの方法で、ターミナル
forEach代わりに通常の
forループを使用したい場合があります。 これには多くの利点があります。効果的に最終的なだけでなく、任意の変数を使用できる、例外をスローできる、デバッグが簡単、短いスタックトレースなどです。一般に、ストリームを作成してすぐに使用する場合、大きな罪はないと思います
forループで。 もちろん、
Iterableを受け入れ、それを数回バイパスできるメソッドに渡さないように注意する必要があります。 例:
public void copyNonEmptyLines(Reader reader, Writer writer) throws IOException { for(String line : StreamEx.ofLines(reader).remove(String::isEmpty)) { writer.write(line); writer.write(System.lineSeparator()); } }
あなたがそれを好きなら、それを使用しますが、注意してください。
キーと値をマップする
多くの場合、値が特定の条件を満たす、またはその逆のすべての
Mapキーを処理する必要があります。 これを直接書くのは少し退屈です:
Map.Entryをいじる必要があり
Map.Entry 。 これを静的メソッド
ofKeys(map, valuePredicate)および
ofValues(map, keyPredicate)のフードの下に隠しました。
Map<String, Role> nameToRole; public Set<String> getEnabledRoleNames() { return StreamEx.ofKeys(nameToRole, Role::isEnabled).toSet(); }
エントリーストリーム
より複雑な
Map処理シナリオのために、別個の
EntryStreamクラスが
EntryStreamさ
EntryStreamオブジェクトのストリーム。
StreamEx機能を部分的に繰り返しますが、キーと値を個別に処理する追加のメソッドも含まれています。 場合によっては、これにより、新しい
Map生成と既存の
Map逆アセンブルの両方が簡単になります。 たとえば、これはMap-Listを反転する方法です(値のリストからの行はキーに分類され、キーは新しい値のリストを形成します)。
public Map<String, List<String>> invert(Map<String, List<String>> map) { return EntryStream.of(map).flatMapValues(List::stream).invert().grouping(); }
flatMapValues使用し
flatMapValues 。これにより、ストリーム
Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - . flatMapValuesに
Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - . Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - . Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - .
Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - . Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - .
Entry<String, List> Entry<String, String> , invert , , grouping — Map .
Map :
public Map<String, String> stringMap(Map<Object, Object> map) { return EntryStream.of(map).mapKeys(String::valueOf).mapValues(String::valueOf).toMap(); }
, :
Map<String, Group> nameToGroup; public Map<String, List<User>> getGroupMembers(Collection<String> groupNames) { return StreamEx.of(groupNames).mapToEntry(nameToGroup::get).nonNullValues().mapValues(Group::getMembers).toMap(); }
mapToEntry EntryStream .
. , - . — GitHub , Maven Central . JavaDoc , . , , - .