按值排序Map <Key,Value>

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

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

有更容易的方法吗?


这是一个通用友好的版本:

public class MapUtil {
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
        list.sort(Entry.comparingByValue());

        Map<K, V> result = new LinkedHashMap<>();
        for (Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }

        return result;
    }
}

重要的提示:

这段代码可以以多种方式破解。 如果您打算使用提供的代码,请务必阅读注释以了解其含义。 例如,值不能再通过它们的键来检索。 ( get总是返回null 。)


看起来比上述所有的要容易得多。 使用TreeMap如下:

public class Testing {
    public static void main(String[] args) {
        HashMap<String, Double> map = new HashMap<String, Double>();
        ValueComparator bvc = new ValueComparator(map);
        TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);

        map.put("A", 99.5);
        map.put("B", 67.4);
        map.put("C", 67.4);
        map.put("D", 67.3);

        System.out.println("unsorted map: " + map);
        sorted_map.putAll(map);
        System.out.println("results: " + sorted_map);
    }
}

class ValueComparator implements Comparator<String> {
    Map<String, Double> base;

    public ValueComparator(Map<String, Double> base) {
        this.base = base;
    }

    // Note: this comparator imposes orderings that are inconsistent with
    // equals.
    public int compare(String a, String b) {
        if (base.get(a) >= base.get(b)) {
            return -1;
        } else {
            return 1;
        } // returning 0 would merge keys
    }
}

输出:

unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}

三个单行答案...

我会使用Google Collections Guava来做到这一点 - 如果您的值是Comparable那么您可以使用

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))

这将为地图创建一个函数(对象)[将任何键作为输入,返回相应的值],然后对它们[值]应用自然(可比较的)排序。

如果他们没有可比性,那么你需要做一些事情

valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map)) 

这些可以应用于TreeMap(如Ordering extends Comparator ),或者在进行一些排序后应用到LinkedHashMap

注意:如果您要使用TreeMap,请记住如果比较== 0,则该项目已经在列表中(如果您有多个比较相同的值,则会发生该项目)。 为了缓解这种情况,您可以像这样将您的密钥添加到比较器中(假定您的密钥和值为Comparable ):

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())

=对键映射的值应用自然排序,并将其与键的自然排序组合

请注意,如果您的键与0比较,这仍然不起作用,但对于大多数comparable项目而言,这应该足够了(因为hashCodeequalscompareTo通常是同步的)

请参阅Ordering.onResultOf()和Functions.forMap()。

履行

所以现在我们已经有了一个可以做我们想要的比较器,我们需要从中得到一个结果。

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

现在这很可能会起作用,但是:

  • 需要完成完成的地图
  • 不要在TreeMap上尝试上面的比较器; 尝试比较插入的键时没有任何意义,因为在插入键之后它没有值时,即它会非常快地断开
  • 对我来说,第一点对我来说有点不合适。 谷歌收藏非常懒惰(这很好:你可以在瞬间完成所有操作;真正的工作是在你开始使用结果时完成的),这需要复制整个地图!

    “完整”答案/按值排序的地图

    不要担心,但; 如果你对以这种方式排序的“活”地图足够痴迷,那么你可以用以下疯狂的东西来解决上述问题中的一个,但不是两个(!):

    注意:这在2012年6月已经发生了很大变化 - 之前的代码无法工作:需要内部HashMap来查找值,而不会在TreeMap.get() - > compare()compare() - >之间创建无限循环get()

    import static org.junit.Assert.assertEquals;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.TreeMap;
    
    import com.google.common.base.Functions;
    import com.google.common.collect.Ordering;
    
    class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
        //A map for doing lookups on the keys for comparison so we don't get infinite loops
        private final Map<K, V> valueMap;
    
        ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
            this(partialValueOrdering, new HashMap<K,V>());
        }
    
        private ValueComparableMap(Ordering<? super V> partialValueOrdering,
                HashMap<K, V> valueMap) {
            super(partialValueOrdering //Apply the value ordering
                    .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
                    .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
            this.valueMap = valueMap;
        }
    
        public V put(K k, V v) {
            if (valueMap.containsKey(k)){
                //remove the key in the sorted set before adding the key again
                remove(k);
            }
            valueMap.put(k,v); //To get "real" unsorted values for the comparator
            return super.put(k, v); //Put it in value order
        }
    
        public static void main(String[] args){
            TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
            map.put("a", 5);
            map.put("b", 1);
            map.put("c", 3);
            assertEquals("b",map.firstKey());
            assertEquals("a",map.lastKey());
            map.put("d",0);
            assertEquals("d",map.firstKey());
            //ensure it's still a map (by overwriting a key, but with a new value) 
            map.put("d", 2);
            assertEquals("b", map.firstKey());
            //Ensure multiple values do not clobber keys
            map.put("e", 2);
            assertEquals(5, map.size());
            assertEquals(2, (int) map.get("e"));
            assertEquals(2, (int) map.get("d"));
        }
     }
    

    当我们放入时,我们确保哈希映射具有比较器的值,然后将它放到TreeSet进行排序。 但在此之前,我们检查哈希映射以查看密钥实际上不是重复的。 此外,我们创建的比较器还将包含密钥,以便重复值不会删除非重复密钥(由于==比较)。 这两项对确保地图合同保持至关重要; 如果你认为你不需要那么做,那么你几乎要完全反转地图( Map<V,K> )。

    构造函数需要被调用为

     new ValueComparableMap(Ordering.natural());
     //or
     new ValueComparableMap(Ordering.from(comparator));
    
    链接地址: http://www.djcxy.com/p/2989.html

    上一篇: Sort a Map<Key, Value> by values

    下一篇: How do I sort a dictionary by value?