Java: Comment convertir une liste en carte


Récemment, j'ai une conversation avec un collègue sur ce qui serait le moyen optimal de convertir List en Map en Java et s'il y a des avantages spécifiques à le faire.

Je veux connaître l'approche de conversion optimale et j'apprécierais vraiment si quelqu'un peut me guider.

Est-ce une bonne approche:

List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
    resultsMap.put((Integer) o[0], (String) o[1]);
}
Author: maytham-ɯɐɥʇʎɐɯ, 2010-11-09

16 answers

List<Item> list;
Map<Key,Item> map = new HashMap<Key,Item>();
for (Item i : list) map.put(i.getKey(),i);

En supposant bien sûr que chaque élément a une méthode getKey() qui renvoie une clé du type approprié.

 151
Author: Jim Garrison, 2017-08-04 19:08:31

Avec java-8, vous serez en mesure de le faire à une ligne à l'aide de ruisseaux, et la Collectors classe.

Map<String, Item> map = 
    list.stream().collect(Collectors.toMap(Item::getKey, item -> item));

Courte démo:

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 + "]";
    }
}

Sortie:

Key-1 => Item [i=1]
Key-2 => Item [i=2]
Key-3 => Item [i=3]
Key-4 => Item [i=4]

Comme indiqué dans les commentaires, vous pouvez utiliser Function.identity() au lieu de item -> item, bien que je trouve i -> i plutôt explicite.

Et pour être complet notez que vous pouvez utiliser un opérateur binaire si votre fonction n'est pas bijective. Par exemple, considérons ceci {[10] } et la fonction de mappage que pour un int valeur, calculer le résultat de celui-ci modulo 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));

Lors de l'exécution de ce code, vous obtiendrez une erreur disant java.lang.IllegalStateException: Duplicate key 1. En effet, 1% 3 est identique à 4% 3 et a donc la même valeur de clé étant donné la fonction de mappage de clé. Dans ce cas, vous pouvez fournir un opérateur de fusion.

En voici un qui additionne les valeurs; (i1, i2) -> i1 + i2; qui peut être remplacé par la référence de méthode Integer::sum.

Map<String, Integer> map = 
    intList.stream().collect(toMap(i -> String.valueOf(i % 3), 
                                   i -> i, 
                                   Integer::sum));

Qui sort maintenant:

0 => 9 (i.e 3 + 6)
1 => 5 (i.e 1 + 4)
2 => 7 (i.e 2 + 5)

J'espère que ça aide! :)

 210
Author: Alexis C., 2016-06-12 09:43:01

Juste au cas où cette question n'est pas fermée en tant que duplicata, la bonne réponse est d'utiliser Google Collections :

Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() {
  public String apply(Role from) {
    return from.getName(); // or something else
  }});
 110
Author: ripper234, 2017-05-23 12:10:41

Depuis Java 8, la réponse par @ZouZou en utilisant le collecteur Collectors.toMap est certainement la façon idiomatique de résoudre ce problème.

Et comme c'est une tâche si courante, nous pouvons en faire un utilitaire statique.

De Cette façon, la solution devient vraiment un one-liner.

/**
 * 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()));
}

Et voici comment vous pouvez l'utiliser sur un List<Student>:

Map<Long, Student> studentsById = toMapBy(students, Student::getId);
 13
Author: glts, 2017-05-23 10:31:36

Un List et Map sont conceptuellement différentes. Un List est une collection ordonnée d'éléments. Les éléments peuvent contenir des doublons et un élément peut ne pas avoir de concept d'identifiant unique (clé). A Map a des valeurs mappées sur des clés. Chaque clé ne peut pointer que vers une seule valeur.

Par conséquent, en fonction des éléments de votre List, il peut ou non être possible de le convertir en Map. Les éléments de votre List n'ont-ils pas de doublons? Chaque article a-t-il une clé unique? Si oui alors il est possible de les mettre dans un Map.

 9
Author: Steve Kuo, 2011-10-31 17:10:42

Il existe également un moyen simple de le faire en utilisant Maps.uniqueIndex(...) à partir de Google goyave bibliothèques

 8
Author: Andrejs, 2012-04-02 22:17:31

Méthode Universelle

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);
}
 5
Author: xxf, 2011-11-23 23:10:48

En utilisant Java 8, vous pouvez faire ce qui suit:

Map<Key, Value> result= results
                       .stream()
                       .collect(Collectors.toMap(Value::getName,Function.identity()));

Value peut-être n'importe quel objet que vous utilisez.

 4
Author: stackFan, 2018-03-01 19:56:50

Sans java-8, vous pourrez le faire dans une ligne Commons collections, et la classe Closure

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);
        }
    });
}};
 3
Author: Vitaliy Oliynyk, 2015-05-15 15:00:25

Alexis a déjà posté une réponse dans Java 8 l'aide de la méthode toMap(keyMapper, valueMapper). Selon doc pour l'implémentation de cette méthode:

Il n'y a aucune garantie sur le type, la mutabilité, la sérialisabilité, ou fil de sécurité de la Carte retournée.

Donc, dans le cas où nous sommes intéressés par une implémentation spécifique de Map interface par exemple {[3] } alors nous pouvons utiliser le formulaire surchargé comme:

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

Bien que l'utilisation soit Function.identity() ou i->i est bien, mais il semble Function.identity() au lieu de i -> ipourrait économiser de la mémoire selon cette réponse .

 3
Author: i_am_zero, 2017-12-10 07:22:17

De nombreuses solutions viennent à l'esprit, en fonction de ce que vous voulez réaliser:

Chaque élément de la liste est une clé et une valeur

for( Object o : list ) {
    map.put(o,o);
}

Les éléments de liste ont quelque chose pour les rechercher, peut-être un nom:

for( MyObject o : list ) {
    map.put(o.name,o);
}

Les éléments de liste ont quelque chose pour les rechercher, et il n'y a aucune garantie qu'ils sont uniques: Utilisez Googles MultiMaps

for( MyObject o : list ) {
    multimap.put(o.name,o);
}

Donnant à tous les éléments la position comme clé:

for( int i=0; i<list.size; i++ ) {
    map.put(i,list.get(i));
}

...

Cela dépend vraiment de ce que vous voulez archive.

Comme vous pouvez le voir dans les exemples, une carte est un mappage d'une clé à une valeur, tandis qu'une liste n'est qu'une série d'éléments ayant chacun une position. Ils ne sont donc tout simplement pas automatiquement convertibles.

 2
Author: Daniel, 2010-11-10 06:17:13

Voici une petite méthode que j'ai écrite dans ce but. Il utilise Validate à partir d'Apache Commons.

Sentir libre de l'utiliser.

/**
 * 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;
}
 2
Author: Kango_V, 2011-10-31 17:11:21

Vous pouvez tirer parti de l'API streams de 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;
  }
}

Pour plus de détails, visitez: http://codecramp.com/java-8-streams-api-convert-list-map/

 2
Author: EMM, 2017-06-09 04:33:48

Un exemple Java 8 pour convertir un List<?> d'objets en un 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()));

Le Code copié à partir de:
https://www.mkyong.com/java8/java-8-convert-list-to-map/

 1
Author: Doss, 2017-05-26 00:42:03

J'aime la réponse de Kango_V, mais je pense que c'est trop complexe. Je pense que c'est plus simple, peut - être trop simple. S'il est incliné, vous pouvez remplacer String par un marqueur générique et le faire fonctionner pour n'importe quel type de clé.

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);
}

Utilisé comme ceci:

        Map<String, PricingPlanAttribute> pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList,
                new ListToMapConverterInterface<PricingPlanAttribute>() {

                    @Override
                    public String getKeyForItem(PricingPlanAttribute item) {
                        return item.getFullName();
                    }
                } );
 0
Author: cs94njw, 2011-09-20 15:42:39

Apache Commons MapUtils.populateMap

Si vous n'utilisez pas Java 8 et que vous ne voulez pas utiliser une boucle explicite pour une raison quelconque, essayez MapUtils.populateMap depuis Apache Commons.

MapUtils.populateMap

, Dire que vous avez une liste de Pairs.

List<ImmutablePair<String, String>> pairs = ImmutableList.of(
    new ImmutablePair<>("A", "aaa"),
    new ImmutablePair<>("B", "bbb")
);

Et vous voulez maintenant une carte de la clé de Pair à l'objet Pair.

Map<String, Pair<String, String>> map = new HashMap<>();
MapUtils.populateMap(map, pairs, new Transformer<Pair<String, String>, String>() {

  @Override
  public String transform(Pair<String, String> input) {
    return input.getKey();
  }
});

System.out.println(map);

Donne la sortie:

{A=(A,aaa), B=(B,bbb)}

Cela étant dit, une boucle for est peut-être plus facile à comprendre. (Ceci ci-dessous donne la même chose de sortie):

Map<String, Pair<String, String>> map = new HashMap<>();
for (Pair<String, String> pair : pairs) {
  map.put(pair.getKey(), pair);
}
System.out.println(map);
 0
Author: typoerrpr, 2018-03-27 02:46:25