it-swarm.com.de

Java: So konvertieren Sie List in Map

Ich habe vor kurzem ein Gespräch mit einem Kollegen darüber geführt, wie die List in Map in Java optimal konvertiert werden kann und ob bestimmte Vorteile bestehen.

Ich möchte den optimalen Umwandlungsansatz kennen und würde mich wirklich freuen, wenn mich jemand führen kann. 

Ist das ein guter Ansatz?

List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
    resultsMap.put((Integer) o[0], (String) o[1]);
}
171
Rachel
List<Item> list;
Map<Key,Item> map = new HashMap<Key,Item>();
for (Item i : list) map.put(i.getKey(),i);

Vorausgesetzt natürlich, dass jedes Element eine getKey()-Methode hat, die einen Schlüssel des richtigen Typs zurückgibt.

166
Jim Garrison

Mit Java-8 können Sie dies in einer Zeile tun, indem Sie streams und die Collectors -Klasse verwenden.

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

Kurze Demo:

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

Ausgabe:

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

Wie in den Kommentaren erwähnt, können Sie Function.identity() anstelle von item -> item verwenden, obwohl i -> i ziemlich eindeutig ist.

Um vollständig zu sein, beachten Sie, dass Sie einen binären Operator verwenden können, wenn Ihre Funktion nicht bijektiv ist. Zum Beispiel betrachten wir diese List und die Mapping-Funktion, die für einen int-Wert das Ergebnis von modulo 3 berechnet:

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

Wenn Sie diesen Code ausführen, erhalten Sie eine Fehlermeldung, die Java.lang.IllegalStateException: Duplicate key 1 sagt. Dies liegt daran, dass 1% 3 mit 4% 3 identisch ist und daher den gleichen Schlüsselwert hat, wenn die Schlüsselzuordnungsfunktion verwendet wird. In diesem Fall können Sie einen Zusammenführungsoperator angeben.

Hier ist eine, die die Werte summiert; (i1, i2) -> i1 + i2;, der durch die Methodenreferenz Integer::sum ersetzt werden kann.

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

was jetzt ausgibt:

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

Ich hoffe es hilft! :)

238
Alexis C.

Nur für den Fall, dass diese Frage nicht als Duplikat abgeschlossen ist, ist die richtige Antwort, Google Collections zu verwenden :

Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() {
  public String apply(Role from) {
    return from.getName(); // or something else
  }});
112
ripper234

Seit Java 8 ist die Antwort von @ZouZou mit dem Collectors.toMap-Collector sicherlich der idiomatische Weg, dieses Problem zu lösen.

Und da dies eine so gewöhnliche Aufgabe ist, können wir es zu einem statischen Dienstprogramm machen.

Auf diese Weise wird die Lösung wirklich zu einem Einzeiler.

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

Und so würden Sie es auf einem List<Student> verwenden:

Map<Long, Student> studentsById = toMapBy(students, Student::getId);
13
glts

Eine List und Map sind konzeptionell verschieden. Eine List ist eine geordnete Sammlung von Artikeln. Die Elemente können Duplikate enthalten, und ein Element verfügt möglicherweise nicht über eine eindeutige Kennung (Schlüssel). Eine Variable Map weist den Tasten Werte zu. Jede Taste kann nur auf einen Wert zeigen.

Abhängig von den Elementen Ihrer List kann es daher möglicherweise nicht möglich sein, sie in eine Map zu konvertieren. Haben Ihre List-Artikel keine Duplikate? Hat jeder Artikel einen eindeutigen Schlüssel? Wenn ja, ist es möglich, sie in eine Map zu setzen.

9
Steve Kuo

Es gibt auch eine einfache Möglichkeit, dies mithilfe von Maps.uniqueIndex (...) aus Google guava - Bibliotheken auszuführen

8
Andrejs

Mit Java 8 können Sie Folgendes tun: 

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

Value kann ein beliebiges Objekt sein, das Sie verwenden.

8
stackFan

Universelle Methode

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
xxf

Alexis hat bereits eine Antwort in Java 8 mit der Methode toMap(keyMapper, valueMapper) gepostet. Gemäß doc für diese Methodenimplementierung:

Es gibt keine Garantie für Typ, Veränderlichkeit, Serialisierbarkeit oder Thread-Sicherheit der Map zurückgegeben.

Für den Fall, dass wir an einer bestimmten Implementierung der Map-Schnittstelle interessiert sind, z. HashMap dann können wir die überladene Form verwenden als:

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

Die Verwendung von Function.identity() oder i->i ist zwar in Ordnung, aber es scheint, als würde Function.identity() anstelle von i -> i etwas Speicherplatz gemäß diesem verwandten answer sparen.

4
i_am_zero

Ohne Java-8 können Sie dies in einer Zeile Commons-Sammlungen und der Closure-Klasse tun

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
Vitaliy Oliynyk

Sie können die Streams-API von Java 8 nutzen.

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

Für weitere Informationen besuchen Sie: http://codecramp.com/Java-8-streams-api-convert-list-map/

2
EMM

Hier ist eine kleine Methode, die ich genau für diesen Zweck geschrieben habe. Es verwendet Validate von Apache Commons.

Fühlen Sie sich frei, es zu benutzen.

/**
 * 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
Kango_V

Je nachdem, was Sie erreichen möchten, kommen Ihnen viele Lösungen in den Sinn:

Jedes Listenelement ist der Schlüssel und Wert

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

Listenelemente haben etwas zum Nachschlagen, vielleicht einen Namen:

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

Listenelemente haben etwas zum Nachschlagen, und es gibt keine Garantie, dass sie eindeutig sind: Verwenden Sie Google MultiMaps .

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

Geben Sie allen Elementen die Position als Schlüssel:

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

...

Es hängt wirklich davon ab, was Sie erreichen wollen.

Wie Sie in den Beispielen sehen können, ist eine Map eine Zuordnung von einem Schlüssel zu einem Wert, während eine Liste nur eine Reihe von Elementen mit jeweils einer Position ist. Sie sind also einfach nicht automatisch konvertierbar.

2
Daniel

Ein Java 8-Beispiel zum Konvertieren eines List<?> von Objekten in einen 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 kopiert von:
https://www.mkyong.com/Java8/Java-8-convert-list-to-map/

1
Doss

wie schon gesagt, in Java-8 haben wir die prägnante Lösung von Collectors:

  list.stream().collect(
         groupingBy(Item::getKey)
        )

sie können auch mehrere Gruppen verschachteln, indem Sie eine andere groupingBy-Methode als zweiten Parameter übergeben:

  list.stream().collect(
         groupingBy(Item::getKey, groupingBy(Item::getOtherKey))
        )

Auf diese Weise haben wir eine mehrstufige Karte wie diese: Map<key, Map<key, List<Item>>>

1

Apache Commons MapUtils.populateMap

Wenn Sie Java 8 nicht verwenden und aus bestimmten Gründen keine explizite Schleife verwenden möchten, versuchen Sie MapUtils.populateMap von Apache Commons. 

MapUtils.populateMap

Angenommen, Sie haben eine Liste von Pairs.

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

Und Sie möchten jetzt eine Karte des Pair-Schlüssels für das Pair-Objekt.

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

gibt Ausgabe:

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

Davon abgesehen ist eine for-Schleife vielleicht einfacher zu verstehen. (Dies ergibt die gleiche Ausgabe):

Map<String, Pair<String, String>> map = new HashMap<>();
for (Pair<String, String> pair : pairs) {
  map.put(pair.getKey(), pair);
}
System.out.println(map);
0
typoerrpr

Ich mag die Antwort von Kango_V, aber ich denke, es ist zu komplex. Ich denke, das ist einfacher - vielleicht zu einfach. Wenn Sie geneigt sind, können Sie String durch eine generische Markierung ersetzen und für jeden Schlüsseltyp verwenden.

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

So benutzt:

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

                    @Override
                    public String getKeyForItem(PricingPlanAttribute item) {
                        return item.getFullName();
                    }
                } );
0
cs94njw