Można użyć
public static <K, V> Map<K, V> toMap(Object... entries) {
if(entries.length % 2 == 1)
throw new IllegalArgumentException("Invalid entries");
return (Map<K, V>)IntStream.range(0, entries.length/2).map(i -> i*2)
.collect(HashMap::new, (m,i)->m.put(entries[i], entries[i+1]), Map::putAll);
}
ale to daje (założony) niezaznaczone ostrzeżenie. Twoja metoda nie może spełnić obietnicy, aby zwrócić prawidłowo wpisaną Map<K, V>
dla tablicy dowolnych obiektów, a co gorsza, nie zawiedzie z wyjątkiem, ale po cichu zwróci niespójną mapę, jeśli przekażesz obiekty niewłaściwego typu.
Czystsze, powszechnie stosowane rozwiązanie jest
public static <K, V> Map<K, V> toMap(
Class<K> keyType, Class<V> valueType, Object... entries) {
if(entries.length % 2 == 1)
throw new IllegalArgumentException("Invalid entries");
return IntStream.range(0, entries.length/2).map(i -> i*2)
.collect(HashMap::new,
(m,i)->m.put(keyType.cast(entries[i]), valueType.cast(entries[i+1])),
Map::putAll);
}
To może być skompilowany bez ostrzeżenia, a poprawność będzie sprawdzana przy starcie. Kod wywołujący musi być dostosowana:
Map<String, Integer> map1 = toMap(String.class, Integer.class, "k1", 1, "k2", 2);
Map<String, String> map2 = toMap(
String.class, String.class, "k1", "v1", "k2", "v2", "k3", "v3");
Poza tym należy określić rzeczywiste typy jak literały klasy, ma tę wadę, że nie wspiera rodzajowe klucza lub wartości typów (jako że nie mogą być wyrażone jako Class
) i wciąż nie ma bezpieczeństwa podczas kompilacji, tylko kontrola czasu wykonywania.
Warto looking at Java 9. Tam będzie można zrobić:
Map<String, Integer> map1 = Map.of("k1", 1, "k2", 2);
Map<String, String> map2 = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
To stworzy niezmienny mapę typu nieokreślonego, zamiast HashMap
, ale interesującym punktem jest API.
Istnieje sposób <K,V> Map.Entry<K,V> entry(K k, V v)
, które mogą być połączone z
<K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)
utworzyć mapę o zmiennej długości (varargs wciąż ograniczone do 255 parametrów, chociaż).
można zaimplementować coś podobnego:
public static <K,V> Map.Entry<K,V> entry(K k, V v) {
return new AbstractMap.SimpleImmutableEntry<>(k, v);
}
public static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries) {
return Arrays.stream(entries)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
Sposób wygoda (s) of
realizowane są jedynym sposobem, można to zrobić z bezpieczeństwem typu: jak przeciążone metody z różną liczbą argumentów, jak
public static <K,V> Map<K,V> of() {
return new HashMap<>();// or Collections.emptyMap() to create immutable maps
}
static <K,V> Map<K,V> of(K k1, V v1) {
return ofEntries(entry(k1, v1));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2) {
return ofEntries(entry(k1, v1), entry(k2, v2));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4));
}
(Java 9 wykonuje cięcie na dziesięć mapowań, jeśli masz więcej, musisz użyć wariantu ofEntries(entry(k1, v1), …)
).
Jeśli zastosujemy się do tego wzorca, należy zachować swoje nazwisko toMap
lub używać tylko map
zamiast -zawijającego „of
”, a nie piszesz interfejs Map
.
Te przeciążenia mogą nie wyglądać bardzo elegancko, ale rozwiązują wszystkie problemy. Możesz napisać kod tak jak w pytaniu, bez określania obiektów Class
, ale zyskujesz na bezpieczeństwie typu kompilacji, a nawet odrzucenia prób wywołania go z nieparzystą liczbą argumentów.
Musisz dokonać cięcia przy określonej liczbie parametrów, ale, jak już wspomniano, nawet warianty nie obsługują nieograniczonych parametrów. A forma ofEntries(entry(…), …)
nie jest taka zła w przypadku większych map.
Kolektor Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
zwraca nieokreśloną typ mapy, która może być nawet niezmienne (choć jest to HashMap
w bieżącej wersji). Jeśli chcesz mieć gwarancję, że instancja HashMap
zostanie zwrócona, musisz zamiast tego użyć Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1,v2)->{throw new IllegalArgumentException("duplicate key");}, HashMap::new)
.
lepiej po prostu użyj dobrej starej pętli 'for' :) – ZhongYu
Możesz znaleźć to, czego szukasz: [Zbierz kolejne pary ze strumienia] (http://stackoverflow.com/questions/20470010/collect-successive -para-z-strumienia). – MikaelF
Odsyłacz: https://stackoverflow.com/questions/31693781/convert-string-array-to-map-using-java-8-lambda-expressions –