Java: Wie Liste Karte konvertieren
-
30-09-2019 - |
Frage
Vor kurzem habe ich Gespräch mit einem Kollegen über das, was der optimalen Weg in Java und wenn es spezifische Vorteile, dies zu tun.
zu konvertierenList
zu Map
wäre
Ich möchte eine optimale Umwandlung Ansatz wissen und würde wirklich schätzen, wenn jemand mich leiten kann.
Ist der gute Ansatz:
List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
resultsMap.put((Integer) o[0], (String) o[1]);
}
Lösung
List<Item> list;
Map<Key,Item> map = new HashMap<Key,Item>();
for (Item i : list) map.put(i.getKey(),i);
natürlich Unter der Annahme, dass jedes Element hat eine getKey()
Methode, dass die Rendite einen Schlüssel vom richtigen Typ.
Andere Tipps
Mit java-8 , können Sie diese mit in einer Zeile tun und Collectors
Klasse.
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 bereits erwähnt in den Kommentaren, können Sie Function.identity()
statt item -> item
verwenden, obwohl ich i -> i
eher explizit finden.
Und vollständige Kenntnis, dass Sie einen binären Operator verwenden können, wenn Ihre Funktion nicht bijektiv ist. Zum Beispiel lassen betrachten die diese List
und die Mapping-Funktion, die für einen int-Wert, um das Ergebnis berechnen davon 3 Modulo:
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, dass java.lang.IllegalStateException: Duplicate key 1
bekommen. Dies liegt daran, dass 1% 3 ist die gleiche wie 4% 3 und damit den gleichen Schlüsselwert der Schlüssel-Mapping-Funktion gegeben haben. In diesem Fall können Sie einen Merge-Operator zur Verfügung stellen.
Hier ist eine, die Summe der Werte; (i1, i2) -> i1 + i2;
, die mit dem Verfahren Referenz Integer::sum
ersetzt werden können.
Map<String, Integer> map =
intList.stream().collect(toMap(i -> String.valueOf(i % 3),
i -> i,
Integer::sum));
, die jetzt gibt:
0 => 9 (i.e 3 + 6)
1 => 5 (i.e 1 + 4)
2 => 7 (i.e 2 + 5)
Hope, es hilft! :)
Für den Fall, diese Frage nicht als Duplikat geschlossen, ist Google Sammlungen zu verwenden:
Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() {
public String apply(Role from) {
return from.getName(); // or something else
}});
Da Java 8, die Antwort von @ZouZou der Collectors.toMap
Kollektor ist sicherlich der idiomatische Weg, um dieses Problem zu lösen.
Und das ist so eine gemeinsame Aufgabe, können wir es in einen statischen Nutzen machen.
Auf diese Weise 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 hier ist, wie Sie es auf einem List<Student>
verwenden würden:
Map<Long, Student> studentsById = toMapBy(students, Student::getId);
Mit Java 8 können Sie wie folgt vorgehen:
Map<Key, Value> result= results
.stream()
.collect(Collectors.toMap(Value::getName,Function.identity()));
Value
kann jedes Objekt, das Sie verwenden.
Ein List
und Map
sind vom Konzept her unterschiedlich. Ein List
ist eine geordnete Sammlung von Gegenständen. Die Elemente können Duplikate enthalten, und ein Element eventuell gar kein Konzept einer eindeutigen Kennung (key). Ein Map
hat Werte Tasten zugeordnet. Jeder Schlüssel kann nur auf einen Wert zeigen.
Daher je nach List
Artikeln, kann es oder auch nicht möglich sein, es zu einem Map
zu konvertieren. Hat Items List
die keine Duplikate haben? Hat jedes Element einen eindeutigen Schlüssel haben? Wenn ja, dann ist es möglich, sie in einem Map
zu setzen.
Es gibt auch eine einfache Möglichkeit, dies zu tun, mit Maps.uniqueIndex (...) von Google Guave Bibliotheken
Alexis hat geschrieben bereits eine Antwort in Java 8 mit der Methode toMap(keyMapper, valueMapper)
. Per doc für diese Methode Implementierung:
Es gibt keine Garantien über die Art, Wandelbarkeit, Serialisierung, oder Thread-Sicherheit der Karte zurückgegeben.
So im Fall sind wir in einer spezifischen Implementierung Map
Schnittstelle zum Beispiel interessieren HashMap
dann können wir die überladene Form wie verwenden:
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
Obwohl entweder Function.identity()
oder i->i
verwendet, ist in Ordnung, aber es scheint Function.identity()
statt i -> i
könnte etwas Speicher gemäß diesem Stand der Antwort speichern .
Universal-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);
}
Ohne Java-8, können Sie diese Commons Sammlungen in einer Zeile zu tun, und die Schließung Klasse
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);
}
});
}};
Viele Lösungen in den Sinn kommen, je nachdem, was Sie wollen achive:
Jeder Listenpunkt ist der Schlüssel und Wert
for( Object o : list ) {
map.put(o,o);
}
Listenelemente haben etwas, sie zu sehen, vielleicht einen Namen:
for( MyObject o : list ) {
map.put(o.name,o);
}
Listenelemente haben etwas, sie zu sehen, und es gibt keine Garantie, dass sie einzigartig sind: Verwenden Sie Googles MultiMaps
for( MyObject o : list ) {
multimap.put(o.name,o);
}
Geben alle Elemente, 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 achive wollen.
Wie Sie aus den Beispielen sehen können, ist eine Karte eine Abbildung von einem Schlüssel auf einen Wert, während eine Liste nur eine Reihe von Elementen ist jeweils eine Position. So sind sie einfach nicht automatisch konvertierbar.
Hier ist eine kleine Methode, die ich für genau diesen Zweck geschrieben. 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;
}
Sie können nutzen die Streams API von 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;
}
}
Weitere Informationen finden Sie unter: http://codecramp.com / java-8-streams-api-convert-Liste-map /
wie schon gesagt, in Java-8 wir die kurze Lösung von Collectors haben:
list.stream().collect(
groupingBy(Item::getKey)
)
und auch können Sie nisten mehrere Gruppe eine andere groupingBy Verfahren als zweiter Parameter übergeben:
list.stream().collect(
groupingBy(Item::getKey, groupingBy(Item::getOtherKey))
)
Auf diese Weise werden wir Multi-Level-Karte haben, wie diese: Map<key, Map<key, List<Item>>>
Ein Java 8 Beispiel einen List<?>
von Objekten in eine Map<k, v>
zu konvertieren:
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 aus:
https://www.mkyong.com/java8/java -8-convert-Liste-to-map /
Ich mag Kango_V Antwort, aber ich denke, es ist zu komplex ist. Ich denke, das ist einfacher - vielleicht zu einfach. Wenn geneigt, könnten Sie String mit einem generischen Marker ersetzen, und machen es für jeden Schlüsseltyp arbeiten.
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);
}
wie folgt verwendet:
Map<String, PricingPlanAttribute> pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList,
new ListToMapConverterInterface<PricingPlanAttribute>() {
@Override
public String getKeyForItem(PricingPlanAttribute item) {
return item.getFullName();
}
} );
Apache Commons MapUtils.populateMap
Wenn Sie 8 verwenden Java nicht und Sie nicht wollen, eine explizite Schleife aus irgendeinem Grund verwenden, versuchen MapUtils.populateMap
von Apache Commons.
Angenommen, Sie haben eine Liste von Pair
s.
List<ImmutablePair<String, String>> pairs = ImmutableList.of(
new ImmutablePair<>("A", "aaa"),
new ImmutablePair<>("B", "bbb")
);
Und Sie wollen jetzt eine Karte des Schlüssels des Pair
zum 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 Ausgang:
{A=(A,aaa), B=(B,bbb)}
Das wird gesagt, eine for
Schleife ist vielleicht leichter zu verstehen. (Diese unten gibt 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);