Подтвердить что ты не робот

Список Java 8 для отображения с потоком

У меня есть коллекция List<Item>. Мне нужно преобразовать его в Map<Integer, Item> Ключ карты должен быть индексом элемента в коллекции. Я не могу понять, как это сделать с потоками. Что-то вроде:

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

Любая помощь?

Поскольку этот вопрос идентифицируется как возможный дубликат, мне нужно добавить, что моя конкретная проблема заключалась в том, как получить позицию элемента в списке и поместить его как ключевое значение

4b9b3361

Ответ 1

Вы можете создать Stream индексов с помощью IntStream, а затем преобразовать их в Map:

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

Ответ 2

Еще одним решением для полноты является использование пользовательского коллектора:

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() или что-то еще).

Ответ 3

Это обновленный ответ и ни одна из проблем, упомянутых в комментариях.

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

Ответ 4

Не чувствуйте, что вам нужно делать все в/с потоком. Я бы просто сделал:

AtomicInteger index = new AtomicInteger();
items.stream().collect(Collectors.toMap(i -> index.getAndIncrement(), i -> i));

Пока вы не будете распараллеливать поток, это будет работать, и это позволит избежать потенциально дорогостоящих и/или проблемных (в случае дубликатов) операций get() и indexOf().

(Вместо AtomicInteger вы не можете использовать переменную int, потому что переменные, используемые из-за пределов лямбда-выражения, должны быть эффективно окончательными. Обратите внимание, что когда uncontested (как в этом случае), AtomicInteger очень быстро и не будет представлять проблемы производительности. Но если это вас беспокоит, вы можете использовать счетчик, не зависящий от потока.)

Ответ 5

Используя стороннюю библиотеку (protonpack, например, но есть и другие), вы можете zip значение со своим индексом и вуаля:

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

хотя getIndex возвращает a long, поэтому вам может потребоваться отбросить его, используя что-то похожее на:

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

Ответ 6

Ответ Eran, как правило, лучший подход для списков с произвольным доступом.

Если ваш 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 гарантирует, что элементы обрабатываются по очереди, по порядку. По этой причине маловероятно, что любое ускорение будет результатом параллельной работы. (Возможно, произойдет некоторое ускорение, если перед конвейером forEachOrdered есть дорогостоящие операции).