2010-11-09 18 views
135

最近我談話有什麼將List轉換爲Map在Java中的最佳方式同事,如果轉換有這樣做的任何特殊利益。的Java:如何清單地圖

我想知道的最佳轉換方法,並會很感激,如果任何人能指導我。

這是好辦法:

List<Object[]> results; 
Map<Integer, String> resultsMap = new HashMap<Integer, String>(); 
for (Object[] o : results) { 
    resultsMap.put((Integer) o[0], (String) o[1]); 
} 
+2

什麼是最佳最佳方式?考慮到某些參數(速度/記憶),進行優化。 – 2010-11-09 20:44:00

+6

列表在概念上與地圖不同 - 地圖具有'關鍵,值'對的概念,而List不包含。鑑於此,目前還不清楚你將如何從列表轉換爲地圖並返回。 – 2010-11-09 20:46:57

+0

@Daniel:通過Optimal,我的意思是在不同所有方式之間以不同方式進行這樣做的最好方式是什麼,所以最好能看到將列表轉換爲地圖的一些不同方式。 – Rachel 2010-11-09 20:50:44

回答

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

假設,當然,每個項目都有一個getKey()方法返回正確類型的密鑰。

+1

您也可以鍵入列表中的位置。 – Jeremy 2010-11-09 20:47:21

+0

@Jim:我需要將'getKey()'設置爲任何特定的參數嗎? – Rachel 2010-11-09 20:49:03

+0

也可以在地圖中的價值,你可以用一個例子來闡述? – Rachel 2010-11-09 20:49:40

2

許多解決方案浮現在腦海中,這取決於你想才達到什麼:

每個列表項目是關鍵,價值

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

列表元素有話要找一找,也許一個名字:

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

List元素有事找他們,他們也不能保證它們是唯一的:使用谷歌MultiMaps

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

給予所有元素的位置作爲重點:

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

...

這真的取決於你想才達到的。

正如你從例子中可以看到,地圖是一個關鍵值的映射,而名單只是一系列具有每一個位置的元素。所以他們根本不能自動兌換。

+0

但是我們可以將列表元素位置視爲關鍵字並將其值放在地圖中,這是一個很好的解決方案嗎? – Rachel 2010-11-09 20:54:35

+0

AFAIK是的! JDK中沒有自動執行的功能,所以您必須自行推出。 – Daniel 2010-11-10 06:17:58

+0

是否有可能用java 8流做最後一個版本(使用數組索引作爲map key)? – Blauhirn 2017-02-04 17:09:38

9

ListMap在概念上是不同的。 A List是項目的有序集合。這些項目可以包含重複項目,並且項目可能沒有任何唯一標識符(關鍵字)的概念。 A Map具有映射到密鑰的值。每個鍵只能指向一個值。

因此,根據您的List的項目,它可能或不可能將其轉換爲Map。你的List的物品沒有重複嗎?每個項目是否有唯一的鑰匙?如果是這樣,那麼可以將它們放入Map

2

這裏有一個小方法,我寫出於這樣的目的。它使用來自Apache Commons的Validate。

隨意使用它。

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

我喜歡Kango_V的回答,但我認爲這太複雜。我認爲這很簡單 - 可能太簡單了。如果傾向於,可以用通用標記替換String,並使其適用於任何Key類型。

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

像這樣來使用:

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

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

通用方法

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

如何使用此?我應該怎樣通過該方法中的'converter'參數? – 2015-10-05 14:48:47

108

以防萬一,這個問題不封閉的重複,the right answer is to use Google Collections

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

這應該是在頂部 – 2013-03-21 10:57:16

+5

「* [番石榴(https://code.google.com/p/guava-libraries/#Important_Warnings)包含**舊的嚴格兼容的超集,棄用谷歌集合庫** 。你不應該再使用這個庫了。*「可能需要更新。 – Tiny 2014-11-24 15:20:28

+2

使用外部庫進行這種簡單的操作是過度的。這是一個非常薄弱的​​標準庫的標誌。在這種情況下,@ jim-garrison的回答是完全合理的。很遺憾,java沒有像「map」和「reduce」這樣有用的方法,但並非完全必要。 – linuxdan 2015-05-12 16:18:44

176

隨着的方式,你就可以做到這一點,使用streams一行,並Collectors類。

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

簡短演示:

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

輸出:

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

正如在評論中指出,你可以使用Function.identity()代替item -> item,雖然我覺得i -> i相當明確。

要完整地說明,如果函數不是雙射的,可以使用二元運算符。例如,讓我們考慮這個List和映射功能,對於一個int值,計算結果是模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)); 

當運行這段代碼,你會得到一個錯誤說java.lang.IllegalStateException: Duplicate key 1。這是因爲1%3與4%3相同,因此在給定密鑰映射函數的情況下具有相同的密鑰值。在這種情況下,您可以提供合併操作符。

這是一個總結價值; (i1, i2) -> i1 + i2;,可以用方法參考Integer::sum代替。

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

現在輸出:

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

希望它能幫助! :)

+8

更好地利用'''的替代項Function.identity() - > item' – 2014-11-04 14:24:24

+0

@EmmanuelTouzery好,'Function.identity()'返回'噸 - >噸;'。 – 2015-02-15 15:13:03

+0

當然,兩者都有效。我想這是一個品味問題。我發現Function.identity()更易於識別。 – 2015-02-15 20:36:02

11

自從Java 8以來,使用Collectors.toMap收集器的answer by @ZouZou無疑是解決此問題的慣用方法。

而且這是一個很常見的任務,我們可以將它變成一個靜態工具。

這樣的解決方案真正成爲一個班輪。

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

而且這裏是你將如何使用它在List<Student>

Map<Long, Student> studentsById = toMapBy(students, Student::getId); 
+0

長。對於這種方法的類型參數的討論參見[我的後續問題(http://stackoverflow.com/q/26691278)。 – glts 2014-11-01 19:40:24

+0

這會引發異常,以防重複鍵。 ......喜歡:線程「main」中的異常java.lang.IllegalStateException:重複鍵....有關詳細信息,請參閱:http://codecramp.com/java-8-streams-api-convert-list-map/ – EMM 2017-06-09 04:35:58

+0

@ EMM當然,正如Javadoc所預期和記錄的那樣。 – glts 2017-06-09 16:11:27

3

不帶Java-8,你就能夠做到這一條線共享的集合,而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); 
     } 
    }); 
}}; 
2

您可以利用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; 
    } 
} 

的流API欲瞭解更多詳情請訪問:http://codecramp.com/java-8-streams-api-convert-list-map/

1

亞歷克西斯已經發布了一個答案Java 8使用方法toMap(keyMapper, valueMapper)。按照doc此方法實現:

上有型,可變性,可串行任何保證或地圖 線程安全返回。

因此,如果我們對Map接口的具體實現感興趣,例如, HashMap那麼我們就可以使用重載形式:

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 

雖然使用或者Function.identity()i->i是不錯,但它似乎的Function.identity()代替i -> i可能有些記憶保存按照此相關answer

0

使用Java 8,你可以做如下:

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

Value可以是你使用任何對象。