سؤال

لقد أجريت مؤخرًا محادثة مع زميل حول ما سيكون الطريقة المثلى للتحويل 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]);
}
هل كانت مفيدة؟

المحلول

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

على افتراض أن كل عنصر لديه ملف getKey() الطريقة التي تُرجع مفتاح النوع الصحيح.

نصائح أخرى

مع , ، ستتمكن من القيام بذلك في سطر واحد باستخدام تيارات, ، و ال 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 ، احسب نتيجة 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)

أتمنى أن يساعد! قون

فقط في حالة عدم إغلاق هذا السؤال كنسخ مكررة ، الإجابة الصحيحة هي استخدام مجموعات Google:

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

منذ جافا 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);

باستخدام Java 8 يمكنك القيام بما يلي:

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

Value يمكن أن يكون أي كائن تستخدمه.

أ List و Map مختلفة من الناحية المفاهيمية. أ List هي مجموعة مرتبة من العناصر. يمكن أن تحتوي العناصر على تكرارات ، وقد لا يكون للبند أي مفهوم لمعرف فريد (مفتاح). أ Map لديه قيم تم تعيينها على المفاتيح. كل مفتاح يمكن أن يشير فقط إلى قيمة واحدة.

لذلك ، اعتمادا على Listعناصر ، قد يكون أو لا يكون من الممكن تحويلها إلى أ Map. هل Listعناصر ليس لها تكرارات؟ هل لكل عنصر مفتاح فريد؟ إذا كان الأمر كذلك فمن الممكن وضعهم في Map.

هناك أيضًا طريقة بسيطة للقيام بذلك باستخدام Maps.UniqueIndex (...) من جوجل المكتبات

لقد نشر الكسيس بالفعل إجابة في جافا 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 قد يحفظ بعض الذاكرة وفقًا لهذا الصلة إجابه.

طريقة عالمية

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

بدون Java-8 ، ستتمكن من القيام بذلك في مجموعات Commons Line ، وفئة الإغلاق

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

تتبادر إلى الذهن العديد من الحلول ، اعتمادًا على ما تريد تحقيقه:

كل عنصر قائمة هو المفتاح والقيمة

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

تحتوي عناصر القائمة على شيء للبحث عنها ، وربما اسم:

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

تحتوي عناصر القائمة على شيء للبحث عنها ، وليس هناك ما يضمن أنها فريدة من نوعها: استخدم googles الورم المتعدد

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

إعطاء جميع العناصر الموقف كمفتاح:

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

...

يعتمد الأمر حقًا على ما تريد تحقيقه.

كما ترون من الأمثلة ، فإن الخريطة هي رسم خرائط من مفتاح إلى قيمة ، في حين أن القائمة هي مجرد سلسلة من العناصر التي لها موضع لكل منهما. لذلك فهي ببساطة لا يمكن تحويلها تلقائيا.

إليك طريقة صغيرة كتبت لهذا الغرض بالضبط. ويستخدم التحقق من صحة من Apache Commons.

استخدمه بحرية.

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

يمكنك الاستفادة من الجداول API من 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;
  }
}

لمزيد من التفاصيل ، تفضل بزيارة: http://codecramp.com/java-8-treams-api-convert-list-map/

كما قيل بالفعل ، في Java-8 لدينا الحل الموجز من قبل جامعي:

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

وأيضًا ، يمكنك عش مجموعة متعددة تمرر طريقة مجموعة أخرى كمعلمة ثانية:

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

وبهذه الطريقة ، سيكون لدينا خريطة متعددة المستوى ، مثل هذا: Map<key, Map<key, List<Item>>>

مثال Java 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/

أحب إجابة 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();
                    }
                } );

Apache Commons MapUtils.populatemap

إذا لم تستخدم Java 8 ولا تريد استخدام حلقة صريحة لسبب ما ، فحاول MapUtils.populateMap من Apache 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);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top