जावा: लिस्ट को मैप में कैसे बदलें


224

हाल ही में मैं क्या परिवर्तित करने के लिए इष्टतम तरीका होगा के बारे में एक सहयोगी के साथ बातचीत Listकरने के लिए Mapजावा में और अगर वहाँ ऐसा करने का कोई विशेष लाभ।

मैं इष्टतम रूपांतरण दृष्टिकोण जानना चाहता हूं और वास्तव में सराहना करूंगा कि क्या कोई मुझे मार्गदर्शन दे सकता है।

क्या यह अच्छा तरीका है:

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

2
सबसे अच्छा तरीका क्या है? अनुकूलन कुछ पैरामीटर (गति / मेमोरी) को ध्यान में रखकर किया जाता है।
डेनियल फथ

6
वैचारिक तरीके से मानचित्र की सूची भिन्न होती है - मानचित्र में 'कुंजी, मूल्य' जोड़ी की धारणा है, जबकि सूची में नहीं है। यह देखते हुए कि यह स्पष्ट नहीं है कि आप वास्तव में सूची से मैप और बैक में कैसे परिवर्तित होंगे।
विक्टर सोरोकिन

1
@ डैनियल: ऑप्टिमल द्वारा, मेरा मतलब था कि सभी तरीकों के बीच अलग-अलग तरीकों से ऐसा करने का सबसे अच्छा तरीका सभी तरीकों के बारे में सुनिश्चित नहीं है और इसलिए सूची को मानचित्र में परिवर्तित करने के कुछ अलग तरीकों को देखना अच्छा होगा।
राहेल


जवाबों:


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

यह मानते हुए कि प्रत्येक आइटम में एक getKey()विधि है जो उचित प्रकार की कुंजी देता है।


1
आप सूची में स्थान पर भी कुंजी लगा सकते हैं।
जेरेमी

@ जिम: क्या मुझे getKey()किसी विशिष्ट पैरामीटर को सेट करने की आवश्यकता है ?
राचेल

इसके अलावा मानचित्र में क्या मूल्य होगा, क्या आप एक उदाहरण के साथ विस्तृत कर सकते हैं?
राचेल

1
@ राशेल - मूल्य सूची में आइटम है, और कुंजी कुछ ऐसा है जो आइटम को अद्वितीय बनाता है, जो आपके द्वारा निर्धारित किया जाता है। जिम का उपयोग getKey()मनमाना था।
जेरेमी

1
आप आकार पहले से जानते हैं इसलिए आप मैप कर सकते हैं <कुंजी, आइटम> नक्शा = नया हैशमैप <कुंजी, आइटम> (सूची.साइज़ ());
विक्टर रोमेरो

316

साथ में , आप स्ट्रीम और 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और मानचित्रण क्रिया करते हैं कि एक अंतर मान के लिए, इसके परिणाम की गणना करें 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));

इस कोड को चलाते समय, आपको एक त्रुटि मिलेगी 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)

आशा करता हूँ की ये काम करेगा! :)


19
Function.identity()इसके बजाय बेहतर उपयोगitem -> item
इमैनुएल टॉज़री

1
@EmmanuelTouzery खैर, Function.identity()रिटर्न t -> t;
एलेक्सिस सी।

2
ज़रूर, दोनों काम करते हैं। मुझे लगता है कि यह स्वाद की बात है। मुझे फंक्शन.सिटी () अधिक तुरंत पहचानने योग्य लगता है।
इमैनुएल टौजरी

ओपी किसी भी pojos को संभाल नहीं सकता है, बस तार और पूर्णांक जिस पर आप शांत नहीं कर सकते ::getKey
फिल्ल ४४४

@ बेलौहिर मुझे पता है, मेरा उदाहरण नीचे दिए गए कस्टम वर्ग पर आधारित है। आप मानों से अपनी कुंजी उत्पन्न करने के लिए जो भी फ़ंक्शन का उपयोग करने के लिए स्वतंत्र हैं।
एलेक्सिस सी।

115

यदि यह प्रश्न डुप्लिकेट के रूप में बंद नहीं है, तो सही उत्तर Google संग्रह का उपयोग करना है :

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

17
यह शीर्ष पर होना चाहिए
मार्टिन एंडरसन 10

6
" अमरूद में पुराने, पदावनत Google संग्रह लाइब्रेरी का कड़ाई से संगत सुपरसेट होता है । आपको अब उस लाइब्रेरी का उपयोग नहीं करना चाहिए। " अपडेट की आवश्यकता हो सकती है।
टिनी

3
इस तरह के एक सरल ऑपरेशन के लिए एक बाहरी पुस्तकालय का उपयोग ओवरकिल है। यह या बहुत कमजोर मानक पुस्तकालय का संकेत है। इस मामले में @ jim-garrison का जवाब पूरी तरह से उचित है। यह दुखद है कि जावा में "मैप" और "कम" जैसे सहायक तरीके नहीं हैं, लेकिन पूरी तरह से आवश्यक नहीं है।
linuxdan

2
यह अमरूद का उपयोग करता है। दुर्भाग्य से अमरूद एंड्रॉइड पर सुपर स्लो है, इसलिए एंड्रॉइड प्रोजेक्ट में इस समाधान का उपयोग नहीं किया जाना चाहिए।
इगोरगानापोलस्की

यदि सूची में आइटम की भूमिका की नक़ल की गई है, तो उपरोक्त कोड अपवाद को फेंक देगा,
Junchen लियू

17

जावा 8 का उपयोग करके आप निम्नलिखित कार्य कर सकते हैं:

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

Value आपके द्वारा उपयोग की जाने वाली कोई भी वस्तु हो सकती है।


16

जावा 8 के बाद से, कलेक्टर का उपयोग करके @ZouZou का जवाबCollectors.toMap निश्चित रूप से इस समस्या को हल करने का मुहावरेदार तरीका है।

और जैसा कि यह इतना सामान्य कार्य है, हम इसे एक स्थिर उपयोगिता में बना सकते हैं।

इस तरह से समाधान वास्तव में वन-लाइनर बन जाता है।

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

इस पद्धति के प्रकार के मापदंडों की चर्चा के लिए मेरा अनुवर्ती प्रश्न देखें
glts

यह मामले में एक अपवाद फेंक देगा, डुप्लिकेट कुंजियाँ हैं। Sth की तरह: सूत्र में अपवाद "मुख्य" java.lang.IllegalStateException: डुप्लिकेट चाबी .... जानकारी के लिए देखें: codecramp.com/java-8-streams-api-convert-list-map
EMM

@EMM बेशक, जैसा कि इरादा और Javadoc में प्रलेखित है।
glts

हां, डुप्लिकेट के मामले को कवर करने के लिए उत्तर को अपडेट किया। समीक्षा करें। धन्यवाद
EMM

10

A Listऔर Mapवैचारिक रूप से भिन्न हैं। A Listआइटम का एक संग्रहित ऑर्डर है। आइटम में डुप्लिकेट हो सकते हैं, और किसी आइटम में किसी विशिष्ट पहचानकर्ता (कुंजी) की कोई अवधारणा नहीं हो सकती है। A Mapमें कुंजियों की मैपिंग की गई है। प्रत्येक कुंजी केवल एक मान को इंगित कर सकती है।

इसलिए, आपके Listआइटमों के आधार पर , इसे a में परिवर्तित करना संभव है या नहीं भी हो सकता है Map। क्या आपके Listआइटम का कोई डुप्लिकेट नहीं है? क्या प्रत्येक आइटम में एक अद्वितीय कुंजी है? यदि ऐसा है तो उन्हें अंदर लाना संभव है Map


10

एलेक्सिस पहले से ही जावा 8 में विधि का उपयोग करके उत्तर पोस्ट कर चुका है toMap(keyMapper, valueMapper)। इस पद्धति के कार्यान्वयन के लिए डॉक्टर के अनुसार :

लौटाए गए मानचित्र के प्रकार, परिवर्तनशीलता, क्रमबद्धता या थ्रेड-सुरक्षा पर कोई गारंटी नहीं है।

इसलिए यदि हम 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यह संबंधित उत्तर के अनुसार कुछ मेमोरी बचा सकता है ।


1
मज़ेदार तथ्य यह है कि 2019 में बड़ी मात्रा में लोगों को अभी भी यह एहसास नहीं है कि वे असली नक्शे के कार्यान्वयन को नहीं जानते हैं जो उन्हें लंबोदर के साथ मिलता है! वास्तव में यह सिर्फ एक उत्तर है जो मुझे जावा 8 लैम्ब्डा के साथ मिला है जिसे मैं उत्पादन पर उपयोग करूंगा।
पावलो

क्या मानचित्र प्रकार निर्दिष्ट करके संग्रह करने का एक तरीका है, लेकिन मर्ज फ़ंक्शन का उपयोग किए बिना?
रोसबर्ग लिन्हारेस


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

इसका उपयोग कैसे करें? मुझे converterविधि में पैरामीटर के रूप में क्या पास करना चाहिए ?
इगोरगानापोलस्की

4

जावा -8 के बिना, आप इसे एक पंक्ति के कॉमन्स संग्रह और क्लोजर क्लास में कर पाएंगे

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

कई समाधान दिमाग में आते हैं, इस पर निर्भर करता है कि आप क्या हासिल करना चाहते हैं:

प्रत्येक सूची आइटम कुंजी और मूल्य है

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

सूची तत्वों में उन्हें देखने के लिए कुछ है, शायद एक नाम:

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

सूची तत्वों में उन्हें देखने के लिए कुछ है, और इस बात की कोई गारंटी नहीं है कि वे अद्वितीय हैं: Googles MultiMaps का उपयोग करें

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

सभी तत्वों को एक कुंजी के रूप में स्थिति देते हुए:

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

...

यह वास्तव में इस बात पर निर्भर करता है कि आप क्या हासिल करना चाहते हैं।

जैसा कि आप उदाहरणों से देख सकते हैं, एक मानचित्र एक कुंजी से मूल्य के लिए मैपिंग है, जबकि एक सूची में प्रत्येक स्थिति वाले तत्वों की एक श्रृंखला है। इसलिए वे बस स्वचालित रूप से परिवर्तनीय नहीं हैं।


लेकिन हम सूची तत्व स्थिति को महत्वपूर्ण मान सकते हैं और उनके मूल्य को मानचित्र में रख सकते हैं, क्या यह एक अच्छा समाधान है?
राहेल 20

AFAIK हाँ! JDK में कोई फ़ंक्शन नहीं है जो स्वचालित रूप से ऐसा करता है, इसलिए आपको अपना स्वयं का रोल करना होगा।
डैनियल

क्या जावा 8 धाराओं के साथ अंतिम संस्करण (मैप की के रूप में एरे इंडेक्स का उपयोग करके) करना संभव है?
फिल्प ४४४

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

2

आप Java 8 की स्ट्रीम API का लाभ उठा सकते हैं।

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

अधिक जानकारी के लिए देखें: http://codecramp.com/java-8-streams-api-convert-list-map/


1

जावा 8 का उदाहरण List<?>वस्तुओं को एक में बदलने के लिए 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()));

कोड यहां से कॉपी किया गया:
https://www.mkyong.com/java8/java-8-convert-list-to-map/


1

पहले से ही कहा, जावा -8 में हमारे पास कलेक्टरों द्वारा संक्षिप्त समाधान है:

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

और यह भी, आप दूसरे समूह के रूप में एक अन्य समूहीकरण विधि से गुजरने वाले कई समूह को घोंसला बना सकते हैं:

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

इस तरह, हमारे पास बहु स्तरीय नक्शा होगा, जैसे: Map<key, Map<key, List<Item>>>



0

मुझे Kango_V का उत्तर पसंद है, लेकिन मुझे लगता है कि यह बहुत जटिल है। मुझे लगता है कि यह सरल है - शायद बहुत सरल है। यदि झुकाव है, तो आप स्ट्रिंग को एक जेनेरिक मार्कर से बदल सकते हैं, और इसे किसी भी कुंजी प्रकार के लिए काम कर सकते हैं।

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

0

अपाचे कॉमन्स MapUtils.populateMap

यदि आप Java 8 का उपयोग नहीं करते हैं और आप किसी कारण से स्पष्ट लूप का उपयोग नहीं करना चाहते हैं, तो MapUtils.populateMapApache Commons से प्रयास करें ।

MapUtils.populateMap

कहते हैं कि आपके पास सूची है Pair

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

और अब आप ऑब्जेक्ट के Pairलिए कुंजी का एक नक्शा चाहते हैं 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);

आउटपुट देता है:

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

यह कहा जा रहा है, एक forलूप शायद समझना आसान है। (यह नीचे एक ही आउटपुट देता है):

Map<String, Pair<String, String>> map = new HashMap<>();
for (Pair<String, String> pair : pairs) {
  map.put(pair.getKey(), pair);
}
System.out.println(map);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.