Заархивировать два списка в неизменяемый multimap в Java 8 с помощью Guava?
Цикл for выглядит как
ImmutableListMultiMap.<Key, Value>Builder builder
= ImmutableListMultiMap.<Key, Value>newBuilder();
for (int i = 0; i < Math.min(keys.length(), values.length()); i++) {
builder.put(keys.at(i), values.at(i));
}
Возможным первым шагом в Guava / Java 8 является
Streams.zip(keys, values, zippingFunction)
Я думаю, что zippingFunction
должен вернуть запись карты, но нет публично конструируемой записи списка. Таким образом," самый " функциональный способ, которым я могу написать это, - это функция zipping, которая возвращает пару, которая, я не уверен, существует в Guava, или возвращает список из двух элементов, который является изменяемым типом, который должным образом не означает, что есть ровно 2 элемента.
Это было бы желательно, если бы я мог создайте запись на карте:
Streams.zip(keys, values, zippingFunction)
.collect(toImmutableListMultimap(e -> e.getKey(), e.getValue())
Это кажется лучшим способом, за исключением того, что это невозможно, и zip в записи и распаковать из записей все еще кажется окольным путем. Есть ли способ сделать это возможным или его можно улучшить?
3 ответа:
Я думаю, что ваш процедурный код уже является наиболее оптимальным решением (как с точки зрения памяти, так и скорости, предполагая списки произвольного доступа). С небольшими исправлениями, чтобы ваш код компилировался, это будет:
ImmutableListMultimap.Builder<Key, Value> builder = ImmutableListMultimap.builder(); for (int i = 0; i < Math.min(keys.size(), values.size()); i++) { builder.put(keys.get(i), values.get(i)); } return builder.build();
Если вы действительно хотите использовать потоки для того, чтобы" быть функциональным", сжатие двух потоков-это путь, но вам все равно придется создать промежуточные" парные " объекты, прежде чем собирать в multimap. Вы утверждаете, что" нет публично конструируемой записи списка", но это не так правда, есть JDK-ы
SimpleImmutableEntry
и гуава тожеMaps.immutableEntry
Вы можете использовать здесь (и они подходят лучше, чем более общиеPair
, которые, на самом деле, не могут быть найдены ни в JDK, ни в Guava.Использование
Streams#zip
требуется передача потоков, поэтому окончательный код будет выглядеть следующим образом:Streams.zip(keys.stream(), values.stream(), SimpleImmutableEntry::new) .collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue));
Если вы открыты для использования других "функциональных" библиотек Java, которые позволяют выполнять больше операций, связанных с потоком, вы можете использовать jOOL и его
Seq.zip
, которые принимают iterable параметры:Seq.zip(keys, values, SimpleImmutableEntry::new) .collect(toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue));
Другой библиотекой будет StreamEx , который предоставляет
EntryStream
- абстракция для потоков пар ключ-значение.
Если ваши списки имеют произвольный доступ, вы можете сделать это без промелькивания:
Map<Key, Value> map = IntStream.range(0, Math.min(keys.size(), values.size())) .boxed() .collect(toImmutableListMultimap(i -> keys[i], i -> values[i]));
Итак, вы почти все сделали правильно! Ниже приведен ваш обновленный код (предположим, что ваши списки имеют тип
Integer
):Но я всегда люблю делать вещи на родном уровне, так как это дает вам больше власти. Смотрите код ниже с помощьюStreams.zip(keys.stream(), values.stream(), AbstractMap.SimpleImmutableEntry::new) .collect(toImmutableListMultimap(Map.Entry<Integer, Integer>::getKey, Map.Entry<Integer, Integer>::getValue) );
collect
:Streams.zip(keys.stream(), values.stream(), (k, v) -> new AbstractMap.SimpleImmutableEntry(k, v)) .collect(ImmutableListMultimap::builder, ImmutableListMultimap.Builder::put, (builder2, builder3) -> builder2.putAll(builder3.build()) ).build();
Примечание:-
builder2.putAll(builder3.build())
BiConsumer будет работать только при использовании параллельного потока. Это поведениеcollect
(Один из моих любимых потоков).