Java: как преобразовать список в карту
недавно у меня был разговор с коллегой о том, что было бы оптимальным способом конвертировать List to Map в Java и если есть какие-то определенные выгоды.
Я хочу знать оптимальный подход преобразования и был бы очень признателен, если кто-нибудь может направлять меня.
это хороший подход:
List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
resultsMap.put((Integer) o[0], (String) o[1]);
}
16 ответов:
List<Item> list; Map<Key,Item> map = new HashMap<Key,Item>(); for (Item i : list) map.put(i.getKey(),i);предполагая, конечно, что каждый элемент имеет
getKey()метод, который возвращает ключ соответствующего типа.
С java-8, вы сможете сделать это в одной строке с помощью потоки и
Collectorsкласса.Map<String, Item> map = list.stream().collect(Collectors.toMap(Item::getKey, item -> item));короткое демо:
import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Test{ public static void main (String [] args){ List<Item> list = IntStream.rangeClosed(1, 4) .mapToObj(Item::new) .collect(Collectors.toList()); //[Item [i=1], Item [i=2], Item [i=3], Item [i=4]] Map<String, Item> map = list.stream().collect(Collectors.toMap(Item::getKey, item -> item)); map.forEach((k, v) -> System.out.println(k + " => " + v)); } } class Item { private final int i; public Item(int i){ this.i = i; } public String getKey(){ return "Key-"+i; } @Override public String toString() { return "Item [i=" + i + "]"; } }выход:
Key-1 => Item [i=1] Key-2 => Item [i=2] Key-3 => Item [i=3] Key-4 => Item [i=4]как отмечено в комментариях, вы можете использовать
Function.identity()вместоitem -> item, хотя я нахожуi -> iдовольно явной.и для полноты обратите внимание, что вы можете использовать двоичный оператор, если ваша функция не является биективной. Например, давайте рассмотрим это
Listи функция отображения, которая для значения int вычисляет результат по модулю 3:List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6); Map<String, Integer> map = intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i));при запуске этого кода, Вы получите сообщение об ошибке
java.lang.IllegalStateException: Duplicate key 1. Это связано с тем, что 1% 3 совпадает с 4% 3 и, следовательно, имеет одно и то же значение ключа, заданное функцией сопоставления ключей. В этом случае можно предоставить оператор слияния.вот тот, который суммирует значения;
(i1, i2) -> i1 + i2;это можно заменить ссылкой на методInteger::sum.Map<String, Integer> map = intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i, Integer::sum));который теперь выводит:
0 => 9 (i.e 3 + 6) 1 => 5 (i.e 1 + 4) 2 => 7 (i.e 2 + 5)надеюсь, что это помогает! :)
на всякий случай этот вопрос не закрыт как дубликат,правильный ответ-использовать коллекции Google:
Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() { public String apply(Role from) { return from.getName(); // or something else }});
начиная с Java 8,ответ @ZouZou С помощью
Collectors.toMapколлектор, безусловно, идиоматический способ решить эту проблему.и поскольку это такая общая задача, мы можем сделать ее статической утилитой.
таким образом, решение действительно становится однострочным.
/** * Returns a map where each entry is an item of {@code list} mapped by the * key produced by applying {@code mapper} to the item. * * @param list the list to map * @param mapper the function to produce the key from a list item * @return the resulting map * @throws IllegalStateException on duplicate key */ public static <K, T> Map<K, T> toMapBy(List<T> list, Function<? super T, ? extends K> mapper) { return list.stream().collect(Collectors.toMap(mapper, Function.identity())); }и вот как бы вы использовали его на
List<Student>:Map<Long, Student> studentsById = toMapBy(students, Student::getId);
A
ListиMapконцептуально разные. АList- это упорядоченная коллекция элементов. Элементы могут содержать дубликаты, и элемент может не иметь понятия уникального идентификатора (ключа). АMapне имеет значения, сопоставленные с ключами. Каждый ключ может указывать только на одно значение.поэтому, в зависимости от вашего
List's элементы, это может или не может быть возможно, чтобы преобразовать его вMap. Делает вашList'ы не дублирует? Есть ли у каждого элемента уникальный ключ? Если это так тогда можно поместить их вMap.
есть также простой способ сделать это с помощью карты.uniqueIndex(...) из Google гуавы библиотеки
универсальный метод
public static <K, V> Map<K, V> listAsMap(Collection<V> sourceList, ListToMapConverter<K, V> converter) { Map<K, V> newMap = new HashMap<K, V>(); for (V item : sourceList) { newMap.put( converter.getKey(item), item ); } return newMap; } public static interface ListToMapConverter<K, V> { public K getKey(V item); }
С помощью Java 8 вы можете сделать следующее:
Map<Key, Value> result= results .stream() .collect(Collectors.toMap(Value::getName,Function.identity()));
Valueможет быть любой объект, который вы используете.
без java-8 вы сможете сделать это в одной строке Commons collections, А класс закрытия
List<Item> list; @SuppressWarnings("unchecked") Map<Key, Item> map = new HashMap<Key, Item>>(){{ CollectionUtils.forAllDo(list, new Closure() { @Override public void execute(Object input) { Item item = (Item) input; put(i.getKey(), item); } }); }};
Алексис уже отправил ответ в Java 8 методом
toMap(keyMapper, valueMapper). Согласно doc для реализации этого метода:нет никаких гарантий на тип, изменчивость, упорядоченность, или потокобезопасность карты возвращается.
поэтому в случае, если мы заинтересованы в конкретной реализации
Mapинтерфейс, например,HashMapтогда мы можем использовать перегруженную форму как:Map<String, Item> map2 = itemList.stream().collect(Collectors.toMap(Item::getKey, //key for map Function.identity(), // value for map (o,n) -> o, // merge function in case of conflict with keys HashMap::new)); // map factory - we want HashMap and not any Map implementationхотя с помощью либо
Function.identity()илиi->iэто нормально, но кажетсяFunction.identity()вместоi -> iможет сохранить некоторую память в соответствии с этим связанным ответ.
многие решения приходят на ум, в зависимости от того, что вы хотите достичь:
каждый элемент списка является ключом и значением
for( Object o : list ) { map.put(o,o); }элементы списка есть что-то, чтобы посмотреть их, может быть, имя:
for( MyObject o : list ) { map.put(o.name,o); }элементы списка имеют что-то искать их, и нет никакой гарантии, что они уникальны: используйте Googles MultiMaps
for( MyObject o : list ) { multimap.put(o.name,o); }дает все элементы, должность ключ:
for( int i=0; i<list.size; i++ ) { map.put(i,list.get(i)); }...
Это действительно зависит от того, чего вы хотите достичь.
как вы можете видеть из примеров, карта-это отображение от ключа к значению, а список-это просто ряд элементов, каждый из которых имеет позицию. Поэтому они просто не могут автоматически конвертироваться.
вот небольшой метод, который я написал для этой цели. Он использует проверку из Apache Commons.
Не стесняйтесь использовать его.
/** * Converts a <code>List</code> to a map. One of the methods of the list is called to retrive * the value of the key to be used and the object itself from the list entry is used as the * objct. An empty <code>Map</code> is returned upon null input. * Reflection is used to retrieve the key from the object instance and method name passed in. * * @param <K> The type of the key to be used in the map * @param <V> The type of value to be used in the map and the type of the elements in the * collection * @param coll The collection to be converted. * @param keyType The class of key * @param valueType The class of the value * @param keyMethodName The method name to call on each instance in the collection to retrieve * the key * @return A map of key to value instances * @throws IllegalArgumentException if any of the other paremeters are invalid. */ public static <K, V> Map<K, V> asMap(final java.util.Collection<V> coll, final Class<K> keyType, final Class<V> valueType, final String keyMethodName) { final HashMap<K, V> map = new HashMap<K, V>(); Method method = null; if (isEmpty(coll)) return map; notNull(keyType, Messages.getString(KEY_TYPE_NOT_NULL)); notNull(valueType, Messages.getString(VALUE_TYPE_NOT_NULL)); notEmpty(keyMethodName, Messages.getString(KEY_METHOD_NAME_NOT_NULL)); try { // return the Method to invoke to get the key for the map method = valueType.getMethod(keyMethodName); } catch (final NoSuchMethodException e) { final String message = String.format( Messages.getString(METHOD_NOT_FOUND), keyMethodName, valueType); e.fillInStackTrace(); logger.error(message, e); throw new IllegalArgumentException(message, e); } try { for (final V value : coll) { Object object; object = method.invoke(value); @SuppressWarnings("unchecked") final K key = (K) object; map.put(key, value); } } catch (final Exception e) { final String message = String.format( Messages.getString(METHOD_CALL_FAILED), method, valueType); e.fillInStackTrace(); logger.error(message, e); throw new IllegalArgumentException(message, e); } return map; }
вы можете использовать API потоков Java 8.
public class ListToMap { public static void main(String[] args) { List<User> items = Arrays.asList(new User("One"), new User("Two"), new User("Three")); Map<String, User> map = createHashMap(items); for(String key : map.keySet()) { System.out.println(key +" : "+map.get(key)); } } public static Map<String, User> createHashMap(List<User> items) { Map<String, User> map = items.stream().collect(Collectors.toMap(User::getId, Function.identity())); return map; } }для получения более подробной информации посетите:http://codecramp.com/java-8-streams-api-convert-list-map/
пример Java 8 для преобразования
List<?>объектов вMap<k, v>:List<Hosting> list = new ArrayList<>(); list.add(new Hosting(1, "liquidweb.com", new Date())); list.add(new Hosting(2, "linode.com", new Date())); list.add(new Hosting(3, "digitalocean.com", new Date())); //example 1 Map<Integer, String> result1 = list.stream().collect( Collectors.toMap(Hosting::getId, Hosting::getName)); System.out.println("Result 1 : " + result1); //example 2 Map<Integer, String> result2 = list.stream().collect( Collectors.toMap(x -> x.getId(), x -> x.getName()));код скопирован из:
https://www.mkyong.com/java8/java-8-convert-list-to-map/
мне нравится ответ Kango_V, но я думаю, что это слишком сложно. Я думаю, что это проще - может быть, слишком просто. Если вы склонны, вы можете заменить строку общим маркером и заставить ее работать для любого типа ключа.
public static <E> Map<String, E> convertListToMap(Collection<E> sourceList, ListToMapConverterInterface<E> converterInterface) { Map<String, E> newMap = new HashMap<String, E>(); for( E item : sourceList ) { newMap.put( converterInterface.getKeyForItem( item ), item ); } return newMap; } public interface ListToMapConverterInterface<E> { public String getKeyForItem(E item); }использовать так:
Map<String, PricingPlanAttribute> pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList, new ListToMapConverterInterface<PricingPlanAttribute>() { @Override public String getKeyForItem(PricingPlanAttribute item) { return item.getFullName(); } } );
Apache Commons MapUtils.populateMap
если вы не используете Java 8 и по какой-то причине не хотите использовать явный цикл, попробуйте
MapUtils.populateMapиз Apache Commons.скажем, у вас есть список
Pairs.List<ImmutablePair<String, String>> pairs = ImmutableList.of( new ImmutablePair<>("A", "aaa"), new ImmutablePair<>("B", "bbb") );и теперь вы хотите карту к
Comments