Самый простой способ объединить два списка в карту (Java)?

Было бы неплохо использовать for (String item: list) , но он будет проходить только через один список, и для другого списка вам понадобится явный iterator. Или вы можете использовать явный iterator для обоих.

Ниже приведен пример проблемы, а вместо этого используется решение с индексированным циклом:

 import java.util.*; public class ListsToMap { static public void main(String[] args) { List names = Arrays.asList("apple,orange,pear".split(",")); List things = Arrays.asList("123,456,789".split(",")); Map map = new LinkedHashMap(); // ordered for (int i=0; i<names.size(); i++) { map.put(names.get(i), things.get(i)); // is there a clearer way? } System.out.println(map); } } 

Вывод:

 {apple=123, orange=456, pear=789} 

Есть ли более ясный способ? Может быть, в API-интерфейсах коллекций?

Поскольку отношение ключевого значения неявно с помощью индекса списка, я думаю, что решение for-loop, которое использует индекс списка явно, на самом деле довольно ясное – и короткое.

Я часто использовал следующую идиому. Я признаю, что это спорно, является ли это понятнее.

 Iterator i1 = names.iterator(); Iterator i2 = things.iterator(); while (i1.hasNext() && i2.hasNext()) { map.put(i1.next(), i2.next()); } if (i1.hasNext() || i2.hasNext()) complainAboutSizes(); 

Преимущество состоит в том, что он также работает для коллекций и подобных вещей без произвольного доступа или без эффективного произвольного доступа, например LinkedList, TreeSets или SQL ResultSets. Например, если вы используете оригинальный алгоритм LinkedLists, у вас есть медленный алгоритм живописца Шлемиеля, который на самом деле нуждается в n * n операциях для списков длины n.

Как было указано 13ren , вы также можете использовать тот факт, что Iterator.next выдает исключение NoSuchElementException, если вы пытаетесь прочитать после конца одного списка, когда длины не совпадают. Таким образом, вы получите терьер, но, может быть, немного запутанный вариант:

 Iterator i1 = names.iterator(); Iterator i2 = things.iterator(); while (i1.hasNext() || i2.hasNext()) map.put(i1.next(), i2.next()); 

Прошло некоторое время с тех пор, как был задан этот вопрос, но в наши дни я частично отношусь к чему-то вроде:

 public static  Map zipToMap(List keys, List values) { return IntStream.range(0, keys.size()).boxed() .collect(Collectors.toMap(keys::get, values::get)); } 

Для тех, кто не знаком с streamами, то, что это делает, получает IntStream от 0 до длины, затем IntStream его, делая его Stream чтобы он мог быть преобразован в объект, а затем собирает их с помощью Collectors.toMap который принимает два поставщиков, один из которых генерирует ключи, другие – значения.

Это может привести к некоторому валидации (например, требование keys.size() будет меньше, чем values.size() ), но оно отлично работает как простое решение.

EDIT: вышеизложенное отлично подходит для чего-либо с постоянным поиском по времени, но если вы хотите что-то, что будет работать в том же порядке (и по-прежнему использовать такой же шаблон), вы можете сделать что-то вроде:

 public static  Map zipToMap(List keys, List values) { Iterator keyIter = keys.iterator(); Iterator valIter = values.iterator(); return IntStream.range(0, keys.size()).boxed() .collect(Collectors.toMap(_i -> keyIter.next(), _i -> valIter.next())); } 

Результат тот же (опять же, отсутствие проверок длины и т. Д.), Но временная сложность не зависит от реализации метода get для любого используемого списка.

Ваше решение выше, конечно, правильно, но ваш вопрос был о ясности, я обращусь к этому.

Самый яркий способ объединить два списка – это превратить комбинацию в метод с хорошим понятным именем. Я только что принял решение и извлек его в метод:

 Map combineListsIntoOrderedMap (List keys, List values) { if (keys.size() != values.size()) throw new IllegalArgumentException ("Cannot combine lists with dissimilar sizes"); Map map = new LinkedHashMap(); for (int i=0; i 

И, конечно, ваш рефакторинга main теперь будет выглядеть так:

 static public void main(String[] args) { List names = Arrays.asList("apple,orange,pear".split(",")); List things = Arrays.asList("123,456,789".split(",")); Map map = combineListsIntoOrderedMap (names, things); System.out.println(map); } 

Я не мог устоять перед проверкой длины.

Как и ясность, я думаю, что есть другие вещи, которые стоит рассмотреть:

  • Правильное отклонение незаконных аргументов, таких как списки разных размеров и null s (посмотрите, что произойдет, если в коде вопроса things ).
  • Возможность обработки списков, которые не имеют быстрого произвольного доступа.
  • Возможность обработки одновременных и синхронизированных коллекций.

Итак, для кода библиотеки, возможно, что-то вроде этого:

 @SuppressWarnings("unchecked") public static  Map linkedZip(List keys, List values) { Object[] keyArray = keys.toArray(); Object[] valueArray = values.toArray(); int len = keyArray.length; if (len != valueArray.length) { throwLengthMismatch(keyArray, valueArray); } Map map = new java.util.LinkedHashMap((int)(len/0.75f)+1); for (int i=0; i 

(Может захотеть проверить, не ставьте несколько одинаковых ключей.)

Нет четкого пути. Мне все еще интересно, есть ли у Apache Commons или Guava что-то подобное. Во всяком случае, у меня была своя статическая утилита. Но это известно о ключевых столкновениях!

 public static  Map map(Collection keys, Collection values) { Map map = new HashMap(); Iterator keyIt = keys.iterator(); Iterator valueIt = values.iterator(); while (keyIt.hasNext() && valueIt.hasNext()) { K k = keyIt.next(); if (null != map.put(k, valueIt.next())){ throw new IllegalArgumentException("Keys are not unique! Key " + k + " found more then once."); } } if (keyIt.hasNext() || valueIt.hasNext()) { throw new IllegalArgumentException("Keys and values collections have not the same size"); }; return map; } 

ArrayUtils # toMap () не объединяет два списка в карту, но делает это для 2-мерного массива (так что не совсем то, что вы ищете, но, может быть, интерес для будущих ссылок …)

Лично я считаю, что простой цикл для итерации по индексам является самым ясным решением, но здесь есть две другие возможности.

Альтернативное решение Java 8, которое позволяет избежать вызова boxed() в IntStream ,

 List keys = Arrays.asList("A", "B", "C"); List values = Arrays.asList("1", "2", "3"); Map map = IntStream.range(0, keys.size()) .collect( HashMap::new, (m, i) -> m.put(keys.get(i), values.get(i)), Map::putAll ); ); 

Вам не нужно даже ограничивать себя Струнами. Немного изменить код от CPerkins:

 Map  combineListsIntoOrderedMap (List keys, List values) { if (keys.size() != values.size()) throw new IllegalArgumentException ("Cannot combine lists with dissimilar sizes"); Map map = new LinkedHashMap(); for (int i=0; i 

}

Используйте Clojure. одна строка – все, что требуется;)

  (zipmap list1 list2) 

Это работает с использованием Eclipse Collections .

 Map map = Maps.adapt(new LinkedHashMap()) .withAllKeyValues( Lists.mutable.of("apple,orange,pear".split(",")) .zip(Lists.mutable.of("123,456,789".split(",")))); System.out.println(map); 

Примечание. Я являюсь коммиттером для коллекций Eclipse.

Другая перспектива заключается в том, чтобы скрыть реализацию. Хотели бы вы, чтобы вызывающая сторона этой функциональности наслаждалась внешним видом расширенного цикла Java?

 public static void main(String[] args) { List names = Arrays.asList("apple,orange,pear".split(",")); List things = Arrays.asList("123,456,789".split(",")); Map map = new HashMap<>(4); for (Map.Entry e : new DualIterator<>(names, things)) { map.put(e.getKey(), e.getValue()); } System.out.println(map); } 

Если да ( Map.Entry выбрано в качестве удобства), то вот полный пример (примечание: он небезопасен для streamов ):

 import java.util.*; /** 

A thread unsafe iterator over two lists to convert them into a map such that keys in first list at a certain index map onto values in the second list at the same index.

Created by kmhaswade on 5/10/16. */ public class DualIterator implements Iterable> { private final List keys; private final List values; private int anchor = 0; public DualIterator(List keys, List values) { // do all the validations here this.keys = keys; this.values = values; } @Override public Iterator> iterator() { return new Iterator>() { @Override public boolean hasNext() { return keys.size() > anchor; } @Override public Map.Entry next() { Map.Entry e = new AbstractMap.SimpleEntry<>(keys.get(anchor), values.get(anchor)); anchor += 1; return e; } }; } public static void main(String[] args) { List names = Arrays.asList("apple,orange,pear".split(",")); List things = Arrays.asList("123,456,789".split(",")); Map map = new LinkedHashMap<>(4); for (Map.Entry e : new DualIterator<>(names, things)) { map.put(e.getKey(), e.getValue()); } System.out.println(map); } }

Он печатает (для каждого требования):

 {apple=123, orange=456, pear=789} 

С Java 8 я бы просто перебирал оба по одному и заполнял карту:

 public  Map combineListsIntoOrderedMap (Iterable keys, Iterable values) { Map map = new LinkedHashMap<>(); Iterator vit = values.iterator(); for (K k: keys) { if (!vit.hasNext()) throw new IllegalArgumentException ("Less values than keys."); map.put(k, vit.next()); } return map; } 

Или вы можете пойти еще дальше с функциональным стилем и сделать:

 /** * Usage: * * Map map2 = new LinkedHashMap<>(); * combineListsIntoOrderedMap(keys, values, map2::put); */ public  void combineListsIntoOrderedMap (Iterable keys, Iterable values, BiConsumer onItem) { Iterator vit = values.iterator(); for (K k: keys) { if (!vit.hasNext()) throw new IllegalArgumentException ("Less values than keys."); onItem.accept(k, vit.next()); } } 

Другое решение Java 8:

Если у вас есть доступ к библиотеке Guava (самая ранняя поддержка streamов в версии 21 [1] ), вы можете сделать:

 Streams.zip(keyList.stream(), valueList.stream(), Maps::immutableEntry) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 

Для меня преимущество этого метода заключается лишь в том, что это один лайнер, и я нашел, что это полезно для моего использования.

  • Как сортировать в алфавитном порядке, игнорируя регистр?
  • Lambda выражение для преобразования массива / Список строк в массив / Список целых чисел
  • Java: лучший способ итерации через коллекцию (здесь ArrayList)
  • Карта заказа Java
  • Самый простой способ преобразования списка в набор в Java
  • Struts2: Обновление значений «Список объектов» внутри карты
  • Как отсортировать ArrayList, используя несколько критериев сортировки?
  • Фильтрация коллекций в C #
  • Быстрее добавлять в коллекцию, сортировать ее или добавлять в сортированную коллекцию?
  • Сортированная коллекция в Java
  • Как инициализировать значения HashSet по построению?
  • Давайте будем гением компьютера.