Java 按值对 Map<Key, Value> 进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/109383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:23:57  来源:igfitidea点击:

Sort a Map<Key, Value> by values

javasortingdictionarycollections

提问by Abe

I am relatively new to Java, and often find that I need to sort a Map<Key, Value>on the values.

我对 Java 比较陌生,经常发现我需要对Map<Key, Value>值进行排序。

Since the values are not unique, I find myself converting the keySetinto an array, and sorting that array through array sortwith a custom comparatorthat sorts on the value associated with the key.

由于值不是唯一的,我发现自己将 转换keySetarray,并通过数组排序使用自定义比较器对该数组进行排序,该比较器对与键关联的值进行排序。

Is there an easier way?

有更容易的方法吗?

回答by devinmoore

From http://www.programmersheaven.com/download/49349/download.aspx

来自http://www.programmersheaven.com/download/49349/download.aspx

private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
    Collections.sort(list, new Comparator<Object>() {
        @SuppressWarnings("unchecked")
        public int compare(Object o1, Object o2) {
            return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<>();
    for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
        Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}

回答by Ryan Delucchi

Depending on the context, using java.util.LinkedHashMap<T>which rememebers the order in which items are placed into the map. Otherwise, if you need to sort values based on their natural ordering, I would recommend maintaining a separate List which can be sorted via Collections.sort().

根据上下文,使用java.util.LinkedHashMap<T>which 可以记住项目在地图中的放置顺序。否则,如果您需要根据自然顺序对值进行排序,我会建议维护一个单独的 List,它可以通过Collections.sort().

回答by p3t0r

The commons-collections library contains a solution called TreeBidiMap. Or, you could have a look at the Google Collections API. It has TreeMultimapwhich you could use.

commons-collections 库包含一个名为TreeBidiMap的解决方案。或者,您可以查看 Google Collections API。它有你可以使用的TreeMultimap

And if you don't want to use these framework... they come with source code.

如果您不想使用这些框架……它们带有源代码。

回答by Dónal

If your Map values implement Comparable (e.g. String), this should work

如果您的 Map 值实现了 Comparable(例如 String),这应该可以工作

Map<Object, String> map = new HashMap<Object, String>();
// Populate the Map
List<String> mapValues = new ArrayList<String>(map.values());
Collections.sort(mapValues);

If the map values themselves don't implement Comparable, but you have an instance of Comparable that can sort them, replace the last line with this:

如果地图值本身没有实现 Comparable,但您有一个可以对它们进行排序的 Comparable 实例,请将最后一行替换为:

Collections.sort(mapValues, comparable);

回答by GHad

For sorting upon the keys I found a better solution with a TreeMap (I will try to get a solution for value based sorting ready too):

为了对键进行排序,我找到了一个更好的 TreeMap 解决方案(我也会尝试准备一个基于值的排序解决方案):

public static void main(String[] args) {
    Map<String, String> unsorted = new HashMap<String, String>();
    unsorted.put("Cde", "Cde_Value");
    unsorted.put("Abc", "Abc_Value");
    unsorted.put("Bcd", "Bcd_Value");

    Comparator<String> comparer = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }};

    Map<String, String> sorted = new TreeMap<String, String>(comparer);
    sorted.putAll(unsorted);
    System.out.println(sorted);
}

Output would be:

输出将是:

{Abc=Abc_Value, Bcd=Bcd_Value, Cde=Cde_Value}

{Abc=Abc_Value, Bcd=Bcd_Value, Cde=Cde_Value}

回答by yoliho

Use java.util.TreeMap.

使用java.util.TreeMap

"The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used."

“地图根据其键的自然顺序进行排序,或者根据地图创建时提供的比较器进行排序,具体取决于使用的构造函数。”

回答by volley

Sorting the keys requires the Comparator to look up each value for each comparison. A more scalable solution would use the entrySet directly, since then the value would be immediately available for each comparison (although I haven't backed this up by numbers).

对键进行排序需要 Comparator 查找每个比较的每个值。一个更具可扩展性的解决方案将直接使用 entrySet,因为这样每个比较都会立即获得该值(尽管我没有用数字来支持它)。

Here's a generic version of such a thing:

这是这种事情的通用版本:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
    final int size = map.size();
    final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
    list.addAll(map.entrySet());
    final ValueComparator<V> cmp = new ValueComparator<V>();
    Collections.sort(list, cmp);
    final List<K> keys = new ArrayList<K>(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}

private static final class ValueComparator<V extends Comparable<? super V>>
                                     implements Comparator<Map.Entry<?, V>> {
    public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

There are ways to lessen memory rotation for the above solution. The first ArrayList created could for instance be re-used as a return value; this would require suppression of some generics warnings, but it might be worth it for re-usable library code. Also, the Comparator does not have to be re-allocated at every invocation.

对于上述解决方案,有一些方法可以减少内存轮换。例如,创建的第一个 ArrayList 可以重新用作返回值;这将需要抑制一些泛型警告,但对于可重用的库代码来说可能是值得的。此外,不必在每次调用时重新分配 Comparator。

Here's a more efficient albeit less appealing version:

这是一个更有效但不那么吸引人的版本:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
    final int size = map.size();
    final List reusedList = new ArrayList(size);
    final List<Map.Entry<K, V>> meView = reusedList;
    meView.addAll(map.entrySet());
    Collections.sort(meView, SINGLE);
    final List<K> keyView = reusedList;
    for (int i = 0; i < size; i++) {
        keyView.set(i, meView.get(i).getKey());
    }
    return keyView;
}

private static final Comparator SINGLE = new ValueComparator();

Finally, if you need to continously access the sorted information (rather than just sorting it once in a while), you can use an additional multi map. Let me know if you need more details...

最后,如果您需要连续访问已排序的信息(而不是偶尔对其进行排序),则可以使用额外的多映射。如果您需要更多详细信息,请告诉我...

回答by GHad

Okay, this version works with two new Map objects and two iterations and sorts on values. Hope, the performs well although the map entries must be looped twice:

好的,这个版本使用两个新的 Map 对象和两个迭代,并对值进行排序。希望,虽然地图条目必须循环两次,但表现良好:

public static void main(String[] args) {
    Map<String, String> unsorted = new HashMap<String, String>();
    unsorted.put("Cde", "Cde_Value");
    unsorted.put("Abc", "Abc_Value");
    unsorted.put("Bcd", "Bcd_Value");

    Comparator<String> comparer = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }};

    System.out.println(sortByValue(unsorted, comparer));

}

public static <K, V> Map<K,V> sortByValue(Map<K, V> in, Comparator<? super V> compare) {
    Map<V, K> swapped = new TreeMap<V, K>(compare);
    for(Entry<K,V> entry: in.entrySet()) {
        if (entry.getValue() != null) {
            swapped.put(entry.getValue(), entry.getKey());
        }
    }
    LinkedHashMap<K, V> result = new LinkedHashMap<K, V>();
    for(Entry<V,K> entry: swapped.entrySet()) {
        if (entry.getValue() != null) {
            result.put(entry.getValue(), entry.getKey());
        }
    }
    return result;
}

The solution uses a TreeMap with a Comparator and sorts out all null keys and values. First, the ordering functionality from the TreeMap is used to sort upon the values, next the sorted Map is used to create a result as a LinkedHashMap that retains has the same order of values.

该解决方案使用带有 Comparator 的 TreeMap 并整理出所有空键和值。首先,来自 TreeMap 的排序功能用于对值进行排序,然后排序后的 Map 用于创建一个结果,因为 LinkedHashMap 保留了相同的值顺序。

Greetz, GHad

格雷茨,GHa

回答by Scott Stanchfield

When I'm faced with this, I just create a list on the side. If you put them together in a custom Map implementation, it'll have a nice feel to it... You can use something like the following, performing the sort only when needed. (Note: I haven't really tested this, but it compiles... might be a silly little bug in there somewhere)

当我面对这个时,我只是在旁边创建一个列表。如果你把它们放在一个自定义 Map 实现中,它会有一个很好的感觉......你可以使用类似下面的东西,只在需要时执行排序。(注意:我还没有真正测试过这个,但它可以编译......可能是某个地方的一个愚蠢的小错误)

(If you want it sorted by both keys and values, have the class extend TreeMap, don't define the accessor methods, and have the mutators call super.xxxxx instead of map_.xxxx)

(如果你希望它按键和值排序,让类扩展 TreeMap,不要定义访问器方法,并让 mutators 调用 super.xxxxx 而不是 map_.xxxx)

package com.javadude.sample;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SortedValueHashMap<K, V> implements Map<K, V> {
    private Map<K, V> map_ = new HashMap<K, V>();
    private List<V> valueList_ = new ArrayList<V>();
    private boolean needsSort_ = false;
    private Comparator<V> comparator_;

    public SortedValueHashMap() {
    }
    public SortedValueHashMap(List<V> valueList) {
        valueList_ = valueList;
    }

    public List<V> sortedValues() {
        if (needsSort_) {
            needsSort_ = false;
            Collections.sort(valueList_, comparator_);
        }
        return valueList_;
    }

    // mutators
    public void clear() {
        map_.clear();
        valueList_.clear();
        needsSort_ = false;
    }

    public V put(K key, V value) {
        valueList_.add(value);
        needsSort_ = true;
        return map_.put(key, value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        map_.putAll(m);
        valueList_.addAll(m.values());
        needsSort_ = true;
    }

    public V remove(Object key) {
        V value = map_.remove(key);
        valueList_.remove(value);
        return value;
    }

    // accessors
    public boolean containsKey(Object key)           { return map_.containsKey(key); }
    public boolean containsValue(Object value)       { return map_.containsValue(value); }
    public Set<java.util.Map.Entry<K, V>> entrySet() { return map_.entrySet(); }
    public boolean equals(Object o)                  { return map_.equals(o); }
    public V get(Object key)                         { return map_.get(key); }
    public int hashCode()                            { return map_.hashCode(); }
    public boolean isEmpty()                         { return map_.isEmpty(); }
    public Set<K> keySet()                           { return map_.keySet(); }
    public int size()                                { return map_.size(); }
    public Collection<V> values()                    { return map_.values(); }
}

回答by Lyudmil

While I agree that the constant need to sort a map is probably a smell, I think the following code is the easiest way to do it without using a different data structure.

虽然我同意对地图进行排序的持续需要可能是一种气味,但我认为以下代码是最简单的方法,无需使用不同的数据结构。

public class MapUtilities {

public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
    Collections.sort(entries, new ByValue<K, V>());
    return entries;
}

private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

}

}

And here is an embarrassingly incomplete unit test:

这是一个令人尴尬的不完整单元测试:

public class MapUtilitiesTest extends TestCase {
public void testSorting() {
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("One", 1);
    map.put("Two", 2);
    map.put("Three", 3);

    List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
    assertEquals("First", "One", sorted.get(0).getKey());
    assertEquals("Second", "Two", sorted.get(1).getKey());
    assertEquals("Third", "Three", sorted.get(2).getKey());
}

}

}

The result is a sorted list of Map.Entry objects, from which you can obtain the keys and values.

结果是 Map.Entry 对象的排序列表,您可以从中获取键和值。