Java: comment convertir une liste en Map

récemment, j'ai une conversation avec un collègue sur ce qui serait la meilleure façon de convertir List en Map en Java et s'il y a des avantages spécifiques de le faire.

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

est cette 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]);
}
158
demandé sur maytham-ɯɐɥʇʎɐɯ 2010-11-09 23:39:32

16 réponses

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
répondu Jim Garrison 2017-08-04 19:08:31

avec , vous pourrez le faire en une seule ligne en utilisant streams , 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 ce List et la fonction de cartographie que pour une valeur int, calculer le résultat de 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 . C'est parce que 1% 3 est le même que 4% 3 et ont donc la même valeur clé étant donné la fonction de cartographie clé. Dans ce cas, vous pouvez fournir un opérateur de fusion.

En voici une qui fait la somme des valeurs; (i1, i2) -> i1 + i2; qui peut être remplacée par la référence de la 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)

Espère que cela aide! :)

210
répondu Alexis C. 2016-06-12 09:43:01

juste au cas où cette question n'est pas fermée comme un duplicata, la bonne réponse est d'utiliser les Collections Google :

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

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

et comme c'est une telle tâche commune, nous pouvons en faire un utilitaire statique.

de cette façon, la solution devient vraiment une doublure.

/**
 * 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 l'utiliseriez sur un List<Student> :

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

Un List et Map sont conceptuellement différentes. Un List est une collection commandée d'articles. Les éléments peuvent contenir des doublons, et un élément peut ne pas avoir de concept d'identificateur unique (clé). Un Map a des valeurs mappées sur les clés. Chaque clé ne peut indiquer qu'une seule valeur.

par conséquent, selon les articles de votre List , il peut être possible ou non de le convertir en un Map . Les articles de votre List n'ont-ils pas les doublons? Chaque article a-t-il une clé unique? Si c'est le cas, il est possible de les mettre dans un Map .

9
répondu Steve Kuo 2011-10-31 17:10:42

il y a aussi une façon simple de le faire en utilisant les cartes .uniqueIndex(...) de Google bibliothèques

8
répondu 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
répondu 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
répondu stackFan 2018-03-01 19:56:50

sans java-8, vous serez en mesure de le faire en une seule ligne collections communes, et la classe de fermeture

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
répondu Vitaliy Oliynyk 2015-05-15 15:00:25

Alexis a déjà posté une réponse dans Java 8 en utilisant la méthode toMap(keyMapper, valueMapper) . Selon doc pour la mise en œuvre de cette méthode:

il n'y a pas de garanties sur le type, la mutabilité, la sériabilité, ou fil de sécurité de la Carte retournée.

donc dans le cas où nous sommes intéressés par une mise en œuvre spécifique de Map interface par exemple HashMap alors nous pouvons utilisez 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 de Function.identity() ou i->i est très bien, mais il semble Function.identity() au lieu de i -> i pourrait sauver une certaine mémoire comme selon cette réponse .

3
répondu i_am_zero 2017-12-10 07:22:17

plusieurs solutions viennent à l'esprit, en fonction de ce que vous voulez accomplir:

chaque élément de la liste est la clé et la valeur

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

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

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

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

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

donnant à tous les éléments le position en tant que clé:

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

...

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

comme vous pouvez le voir dans les exemples, une carte est une correspondance d'une clé à une valeur, tandis qu'une liste est juste une série d'éléments ayant une position chacun. Ils ne sont donc pas automatiquement convertibles.

2
répondu Daniel 2010-11-10 06:17:13

Voici une petite méthode que j'ai écrite dans ce but précis. Il utilise Validate from Apache Commons.

N'hésitez pas à 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
répondu Kango_V 2011-10-31 17:11:21

vous pouvez utiliser 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
répondu EMM 2017-06-09 04:33:48

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

code copié de:

https://www.mkyong.com/java8/java-8-convert-list-to-map /

1
répondu 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. Si vous êtes 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
répondu 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 d'Apache Commons.

MapUtils.populateMap

dites que vous avez une liste de Pair S.

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

et vous voulez maintenant une carte de la clé Pair pour 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 dit, une boucle for est peut-être plus facile à comprendre. (Ce qui suit donne le même résultat):

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