次のクラスがあります。
public class Item {
int id;
String name;
// few other fields, contructor, getters and setters
}
アイテムのリストがあります。リストを反復処理して、特定のIDを持つインスタンスを見つけたいです。私はストリームを介してそれをやろうとしています。
public void foobar() {
List<Item> items = getItemList();
List<Integer> ids = getIdsToLookup();
int id, i = ids.size() - 1;
while (i >= 0) {
id = ids.get(i);
Optional<Item> item = items
.stream()
.filter(a -> a.getId() == id)
.findFirst();
// do stuff
i--;
}
}
これはリストを反復処理し、必要な要素を取得する最良の方法ですか?また、ラムダ式で使用される変数が最終または実質的に最終でなければならないというidのフィルター行でエラーが発生します。たぶん、whileループ内でidを定義して、例外を取り除くことができます。ありがとう。
検索するIDがたくさんある場合は、各IDに対して線形検索を行うのではなく、単一のパスでそれを行うソリューションを使用することをお勧めします。
_Map<Integer,Optional<Item>> map=ids.stream()
.collect(Collectors.toMap(id -> id, id -> Optional.empty()));
items.forEach(item ->
map.computeIfPresent(item.getId(), (i,o)->o.isPresent()? o: Optional.of(item)));
for(ListIterator<Integer> it=ids.listIterator(ids.size()); it.hasPrevious();) {
map.get(it.previous()).ifPresent(item -> {
// do stuff
});
}
_
最初のステートメントは、idsリストからマップを作成し、各検索IDを空のOptional
にマッピングします。
2番目のステートメントは、forEach
を使用してアイテムを反復処理し、各アイテムについて、そのidから空のOptional
へのマッピングがあるかどうかを確認し、そのようなマッピングがある場合はすべてを1つの操作でカプセル化するOptional
に置き換えます。 computeIfPresent
。
最後のfor
ループは、ids
リストを逆順に繰り返します。これは、それらをその順序で処理し、空でないOptional
がある場合にアクションを実行したいためです。マップはリストにあるすべてのIDで初期化されているため、get
はnull
を返すことはありません。IDがOptional
リストにない場合、空のitems
を返します。
そのようにして、Map
のルックアップにO(1)
時間の複雑さがあると仮定すると(これは一般的な実装の場合です)、ネット時間の複雑さはO(m×n)
からO(m+n)
...
次のようなものを使用してみてください:
ids.forEach(id ->
list.stream()
.filter(p -> p.getId() == id)
.findFirst()
.ifPresent(p -> {
// do stuff here
});
);
ここでオプションは、フィルターメソッドが空のストリームを返すことができることを示しています。
ストリームに固執して逆方向に反復したい場合は、次のようにできます。
_IntStream.iterate(ids.size() - 1, i -> i - 1)
.limit(ids.size())
.map(ids::get) // or .map(i -> ids.get(i))
.forEach(id -> items.stream()
.filter(item -> item.getId() == id)
.findFirst().ifPresent(item -> {
// do stuff
}));
_
このコードはあなたのものと同じです。
シードids.size() - 1
で始まり、逆方向に反復します。 int
sの初期ストリームのサイズはlimit()
で制限されているため、負のint
sはなく、ストリームはids
。次に、map()
操作がインデックスをid
リストのi番目の位置にある実際のids
に変換します(これはids.get(i)
)。最後に、コードと同じ方法で、アイテムがitems
リストで検索されます。
特定のIDごとに最大1つのアイテムを検索し、見つかったアイテムに対して何かを実行しますか?もう少しパフォーマンスが向上します。
Set<Integer> idsToLookup = new HashSet<>(getIdsToLookup()); // replace list with Set
items.stream()
.filter(e -> idsToLookup.remove(e.getId()))
.forEach(
/* doing something */
);