web-dev-qa-db-ja.com

Java 8ストリームを使用してマップするリスト

List<Item>コレクションがあります。マップをMap<Integer, Item>に変換する必要があります。マップのキーは、コレクション内のアイテムのインデックスでなければなりません。ストリームでこれを行う方法はわかりません。何かのようなもの:

items.stream().collect(Collectors.toMap(...));

助けがありますか?

この質問は重複する可能性があると識別されているので、具体的な問題はリスト内のアイテムの位置を取得してキー値として配置する方法を追加する必要があります

49
Nikolay

Streamを使用してIntStreamインデックスを作成し、それらをMapに変換できます。

Map<Integer,Item> map = 
    IntStream.range(0,items.size())
             .boxed()
             .collect(Collectors.toMap (i -> i, i -> items.get(i)));
50
Eran

完全を期すためのもう1つのソリューションは、カスタムコレクターを使用することです。

public static <T> Collector<T, ?, Map<Integer, T>> toMap() {
    return Collector.of(HashMap::new, (map, t) -> map.put(map.size(), t), 
            (m1, m2) -> {
                int s = m1.size();
                m2.forEach((k, v) -> m1.put(k+s, v));
                return m1;
            });
}

使用法:

Map<Integer, Item> map = items.stream().collect(toMap());

この解決策は並列処理に適しており、ソースに依存しません(ランダムアクセスやFiles.lines()などを使用せずにリストを使用できます)。

12
Tagir Valeev

これは更新された回答であり、コメントに記載されている問題はありません。

Map<Integer,Item> outputMap = IntStream.range(0,inputList.size()).boxed().collect(Collectors.toMap(Function.identity(), i->inputList.get(i)));
7
i_am_zero

Eran's answer は、通常、ランダムアクセスリストに最適なアプローチです。

Listがランダムアクセスでない場合、またはStreamの代わりにListを持っている場合、forEachOrderedを使用できます。

Stream<Item> stream = ... ;
Map<Integer, Item> map = new HashMap<>();
AtomicInteger index = new AtomicInteger();
stream.forEachOrdered(item -> map.put(index.getAndIncrement(), item));

これは、宛先マップがスレッドセーフではなく、副作用として操作されている場合でも、ストリームが並列である場合は安全です。 forEachOrderedは、アイテムが順番に1つずつ処理されることを保証します。このため、並列実行によって速度が向上することはほとんどありません。 (パイプラインにforEachOrderedの前に高価な操作がある場合、多少の高速化があります。)

1
Stuart Marks

サードパーティのライブラリ( protonpack ですが、他にもあります)を使用すると、インデックスと出来事でZip値を取得できます:

StreamUtils.zipWithIndex(items.stream())
    .collect(Collectors.toMap(Indexed::getIndex, Indexed::getValue));

getIndexlongを返しますが、次のようなものを使用してキャストする必要がある場合があります。

i -> Integer.valueOf((int) i.getIndex())
1
njzk2