按值对HashMap进行排序[重复]

Posted

技术标签:

【中文标题】按值对HashMap进行排序[重复]【英文标题】:Sorting HashMap by values [duplicate] 【发布时间】:2011-12-28 11:52:54 【问题描述】:

我需要根据存储在其中的值对我的HashMap 进行排序。 HashMap 包含存储在手机中的联系人姓名。

我还需要在对值进行排序后立即自动对键进行排序,或者您可以说键和值绑定在一起,因此值的任何更改都应反映在键中。

HashMap<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"froyo");
map.put(2,"abby");
map.put(3,"denver");
map.put(4,"frost");
map.put(5,"daisy");

需要的输出:

2,abby;
5,daisy;
3,denver;
4,frost;
1,froyo;

【问题讨论】:

【参考方案1】:

基本上你不会。 HashMap 基本上是无序的。您可能在排序中看到的任何模式都应该被依赖。

有诸如TreeMap 之类的排序映射,但它们传统上是按键排序而不是按值排序。按值排序相对不常见——尤其是多个键可以具有相同的值。

您能否为您正在尝试做的事情提供更多背景信息?如果您真的只存储键的数字(作为字符串),那么 SortedSet (例如 TreeSet)可能适合您?

或者,您可以将两个单独的集合存储在一个类中以同时更新它们?

【讨论】:

例如,对图像上出现的颜色进行排序。它必须很快,因为我们可以有 max_int 颜色。 @RafaelSanches:尚不清楚您评论的上下文是什么。在这种情况下 map 会是什么?你可能想问一个新问题。 我只是举一个例子,它有助于以最高效的方式按值对 hashmap 进行排序。 我们可以使用这样的东西吗? Arrays.sort(hm.values().toArray()); @VedPrakash:是的,如果您使用总分作为键存储地图,您不应该期望能够存储两个总分相同的值。我建议您提出一个新问题,详细说明您所面临的问题。【参考方案2】:

假设是 Java,你可以像这样对 hashmap 进行排序:

public LinkedHashMap<Integer, String> sortHashMapByValues(
        HashMap<Integer, String> passedMap) 
    List<Integer> mapKeys = new ArrayList<>(passedMap.keySet());
    List<String> mapValues = new ArrayList<>(passedMap.values());
    Collections.sort(mapValues);
    Collections.sort(mapKeys);

    LinkedHashMap<Integer, String> sortedMap =
        new LinkedHashMap<>();

    Iterator<String> valueIt = mapValues.iterator();
    while (valueIt.hasNext()) 
        String val = valueIt.next();
        Iterator<Integer> keyIt = mapKeys.iterator();

        while (keyIt.hasNext()) 
            Integer key = keyIt.next();
            String comp1 = passedMap.get(key);
            String comp2 = val;

            if (comp1.equals(comp2)) 
                keyIt.remove();
                sortedMap.put(key, val);
                break;
            
        
    
    return sortedMap;

只是一个启动示例。这种方式更有用,因为它对 HashMap 进行排序并保留重复值。

【讨论】:

查看编辑后的版本。我不认为 collections.sort(mapvalues) 会解决问题 这段代码已经根据keys排列了hashmap。我想要的是:(2,abby; 5,daisy; 3,denver; 4,frost; 1,froyo;)ie值是根据排列的到他们的首字母缩写,变化会反映在键中...... 我稍微编辑了您的代码,它可以按预期工作。非常感谢。 请注意,由于在两个 while 循环中重复查找值,给定算法的时间复杂度为 O(n^2)。将 Entry Set 转换为 List,然后根据比较器对 List 进行排序将是一种更有效的解决方案。 这种方法的主要问题是由于嵌套的 while 循环,它需要二次时间。有更好的答案,比如来自 Rais Alarm 的优秀答案。【参考方案3】:

试试下面的代码,它对我来说很好。您可以选择升序和降序

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class SortMapByValue

    public static boolean ASC = true;
    public static boolean DESC = false;

    public static void main(String[] args)
    

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<String, Integer>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 80);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByComparator(unsortMap, ASC);
        printMap(sortedMapAsc);


        System.out.println("After sorting descindeng order......");
        Map<String, Integer> sortedMapDesc = sortByComparator(unsortMap, DESC);
        printMap(sortedMapDesc);

    

    private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, final boolean order)
    

        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());

        // Sorting the list based on values
        Collections.sort(list, new Comparator<Entry<String, Integer>>()
        
            public int compare(Entry<String, Integer> o1,
                    Entry<String, Integer> o2)
            
                if (order)
                
                    return o1.getValue().compareTo(o2.getValue());
                
                else
                
                    return o2.getValue().compareTo(o1.getValue());

                
            
        );

        // Maintaining insertion order with the help of LinkedList
        Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
        for (Entry<String, Integer> entry : list)
        
            sortedMap.put(entry.getKey(), entry.getValue());
        

        return sortedMap;
    

    public static void printMap(Map<String, Integer> map)
    
        for (Entry<String, Integer> entry : map.entrySet())
        
            System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
        
    

编辑:版本 2

使用了新的 java 特性,例如 stream for-each 等

如果值相同,地图将按键排序

 import java.util.*;
 import java.util.Map.Entry;
 import java.util.stream.Collectors;

 public class SortMapByValue

 
    private static boolean ASC = true;
    private static boolean DESC = false;
    public static void main(String[] args)
    

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 20);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByValue(unsortMap, ASC);
        printMap(sortedMapAsc);


        System.out.println("After sorting descending order......");
        Map<String, Integer> sortedMapDesc = sortByValue(unsortMap, DESC);
        printMap(sortedMapDesc);
    

    private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap, final boolean order)
    
        List<Entry<String, Integer>> list = new LinkedList<>(unsortMap.entrySet());

        // Sorting the list based on values
        list.sort((o1, o2) -> order ? o1.getValue().compareTo(o2.getValue()) == 0
                ? o1.getKey().compareTo(o2.getKey())
                : o1.getValue().compareTo(o2.getValue()) : o2.getValue().compareTo(o1.getValue()) == 0
                ? o2.getKey().compareTo(o1.getKey())
                : o2.getValue().compareTo(o1.getValue()));
        return list.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new));

    

    private static void printMap(Map<String, Integer> map)
    
        map.forEach((key, value) -> System.out.println("Key : " + key + " Value : " + value));
    

【讨论】:

不错的答案,使用集合工具以正确的方式编写。 针对我的问题进行了尝试,发现需要对您的比较器逻辑进行轻微修改,您没有检查应该添加的空检查,因为地图值可以包含空元素。 有用且清晰的答案。 我发现这个解决方案简单易懂。 如果我想按照value的长度来排序,比如String呢?【参考方案4】:
package SortedSet;

import java.util.*;

public class HashMapValueSort 
public static void main(String[] args)
    final Map<Integer, String> map = new HashMap<Integer,String>();
    map.put(4,"Mango");
    map.put(3,"Apple");
    map.put(5,"Orange");
    map.put(8,"Fruits");
    map.put(23,"Vegetables");
    map.put(1,"Zebra");
    map.put(5,"Yellow");
    System.out.println(map);
    final HashMapValueSort sort = new HashMapValueSort();
    final Set<Map.Entry<Integer, String>> entry = map.entrySet();
    final Comparator<Map.Entry<Integer, String>> comparator = new Comparator<Map.Entry<Integer, String>>() 
        @Override
        public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) 
            String value1 = o1.getValue();
            String value2 = o2.getValue();
            return value1.compareTo(value2);
        
    ;
    final SortedSet<Map.Entry<Integer, String>> sortedSet = new TreeSet(comparator);
    sortedSet.addAll(entry);
    final Map<Integer,String> sortedMap =  new LinkedHashMap<Integer, String>();
    for(Map.Entry<Integer, String> entry1 : sortedSet )
        sortedMap.put(entry1.getKey(),entry1.getValue());
    
    System.out.println(sortedMap);


【讨论】:

不错的一个。谢谢你。【参考方案5】:
public static TreeMap<String, String> sortMap(HashMap<String, String> passedMap, String byParam) 
    if(byParam.trim().toLowerCase().equalsIgnoreCase("byValue")) 
        // Altering the (key, value) -> (value, key)
        HashMap<String, String> newMap =  new HashMap<String, String>();
        for (Map.Entry<String, String> entry : passedMap.entrySet()) 
            newMap.put(entry.getValue(), entry.getKey());
        
        return new TreeMap<String, String>(newMap);
    
    return new TreeMap<String, String>(passedMap);

【讨论】:

【参考方案6】:

在 Java 8 中:

Map<Integer, String> sortedMap = 
     unsortedMap.entrySet().stream()
    .sorted(Entry.comparingByValue())
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                              (e1, e2) -> e1, LinkedHashMap::new));

【讨论】:

这个排序可以倒置吗?我需要的值从大到小 @AquariusPower 查看docs.oracle.com/javase/8/docs/api/java/util/… 的reversed 方法您可以将其应用于comparingByValue() 返回的比较器 Entry.comparingByValue().reversed() 的返回与.sorted(...) 预期的参数不兼容:(,我最终得到了一个反向循环for 超过.entrySet().toArray(),可能是演员可以解决它,我需要更多时间来测试:) @AquariusPower 你不需要强制转换,只需给编译器一个泛型类型的提示:Entry.&lt;Integer, String&gt;comparingByValue().reversed() 而这种编程方式正是我讨厌jdk8的原因。 getKey,getValue, (e1,e2)->e1, ::new... 然后我们可以在火边召唤“wooloh loh”并召唤夜之精灵...【参考方案7】:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

public class CollectionsSort 

    /**
     * @param args
     */`enter code here`
    public static void main(String[] args) 
        // TODO Auto-generated method stub

        CollectionsSort colleciotns = new CollectionsSort();

        List<combine> list = new ArrayList<combine>();
        HashMap<String, Integer> h = new HashMap<String, Integer>();
        h.put("nayanana", 10);
        h.put("lohith", 5);

        for (Entry<String, Integer> value : h.entrySet()) 
            combine a = colleciotns.new combine(value.getValue(),
                    value.getKey());
            list.add(a);
        

        Collections.sort(list);
        for (int i = 0; i < list.size(); i++) 
            System.out.println(list.get(i));
        
    

    public class combine implements Comparable<combine> 

        public int value;
        public String key;

        public combine(int value, String key) 
            this.value = value;
            this.key = key;
        

        @Override
        public int compareTo(combine arg0) 
            // TODO Auto-generated method stub
            return this.value > arg0.value ? 1 : this.value < arg0.value ? -1
                    : 0;
        

        public String toString() 
            return this.value + " " + this.key;
        
    


【讨论】:

请在回答之前确保您的代码运行正确。还可以考虑添加 cmets,以便提问者更好地理解您的答案。【参考方案8】:

找到了解决方案,但如果地图尺寸较大,则不确定性能,这对正常情况很有用。

   /**
     * sort HashMap<String, CustomData> by value
     * CustomData needs to provide compareTo() for comparing CustomData
     * @param map
     */

    public void sortHashMapByValue(final HashMap<String, CustomData> map) 
        ArrayList<String> keys = new ArrayList<String>();
        keys.addAll(map.keySet());
        Collections.sort(keys, new Comparator<String>() 
            @Override
            public int compare(String lhs, String rhs) 
                CustomData val1 = map.get(lhs);
                CustomData val2 = map.get(rhs);
                if (val1 == null) 
                    return (val2 != null) ? 1 : 0;
                 else if (val1 != null) && (val2 != null)) 
                    return = val1.compareTo(val2);
                
                else 
                    return 0;
                
            
        );

        for (String key : keys) 
            CustomData c = map.get(key);
            if (c != null) 
                Log.e("key:"+key+", CustomData:"+c.toString());
             
        
    

【讨论】:

其中的错字应该是 else if ((val1 != null) && (val2 != null)) return val1.compareTo(val2); 在比较方法中【参考方案9】:

如果您只需要最终结果,可以使用 temp TreeMap 作为一种简单的解决方案:

TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>();
for (Map.Entry entry : map.entrySet()) 
    sortedMap.put((String) entry.getValue(), (Integer)entry.getKey());

这将使您的字符串作为 sortedMap 的键进行排序。

【讨论】:

这只有在所有整数值都是唯一的情况下才有效——否则,你会覆盖字符串。【参考方案10】:
package com.naveen.hashmap;

import java.util.*;
import java.util.Map.Entry;

public class SortBasedonValues 

    /**
     * @param args
     */
    public static void main(String[] args) 

        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("Naveen", 2);
        hm.put("Santosh", 3);
        hm.put("Ravi", 4);
        hm.put("Pramod", 1);
        Set<Entry<String, Integer>> set = hm.entrySet();
        List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
                set);
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() 
            public int compare(Map.Entry<String, Integer> o1,
                    Map.Entry<String, Integer> o2) 
                return o2.getValue().compareTo(o1.getValue());
            
        );

        for (Entry<String, Integer> entry : list) 
            System.out.println(entry.getValue());

        

    

【讨论】:

我尝试执行此代码,使用import java.util.Map.Entry;' 运行良好但相同的代码无法使用import java.util.*; 运行,据我所知,后者包括前者。那为什么会报错呢? 简洁优雅【参考方案11】:
map.entrySet().stream()
                .sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
                .forEach(k -> System.out.println(k.getKey() + ": " + k.getValue()));

【讨论】:

对我来说最易读的解决方案并完成工作,例如运行按值排序的 HashMap 简洁明了;优雅易读。 这按地图值的降序打印,我怎样才能强制它升序? 从 k1.getValue() @Rohan 中删除减号【参考方案12】:

我扩展了一个 TreeMap 并覆盖了 entrySet() 和 values() 方法。键和值需要是可比较的。

关注代码:

public class ValueSortedMap<K extends Comparable, V extends Comparable> extends TreeMap<K, V> 

    @Override
    public Set<Entry<K, V>> entrySet() 
        Set<Entry<K, V>> originalEntries = super.entrySet();
        Set<Entry<K, V>> sortedEntry = new TreeSet<Entry<K, V>>(new Comparator<Entry<K, V>>() 
            @Override
            public int compare(Entry<K, V> entryA, Entry<K, V> entryB) 
                int compareTo = entryA.getValue().compareTo(entryB.getValue());
                if(compareTo == 0) 
                    compareTo = entryA.getKey().compareTo(entryB.getKey());
                
                return compareTo;
            
        );
        sortedEntry.addAll(originalEntries);
        return sortedEntry;
    

    @Override
    public Collection<V> values() 
        Set<V> sortedValues = new TreeSet<>(new Comparator<V>()
            @Override
            public int compare(V vA, V vB) 
                return vA.compareTo(vB);
            
        );
        sortedValues.addAll(super.values());
        return sortedValues;
    

单元测试:

public class ValueSortedMapTest 

    @Test
    public void basicTest() 
        Map<String, Integer> sortedMap = new ValueSortedMap<>();
        sortedMap.put("A",3);
        sortedMap.put("B",1);
        sortedMap.put("C",2);

        Assert.assertEquals("B=1, C=2, A=3", sortedMap.toString());
    

    @Test
    public void repeatedValues() 
        Map<String, Double> sortedMap = new ValueSortedMap<>();
        sortedMap.put("D",67.3);
        sortedMap.put("A",99.5);
        sortedMap.put("B",67.4);
        sortedMap.put("C",67.4);

        Assert.assertEquals("D=67.3, B=67.4, C=67.4, A=99.5", sortedMap.toString());
    


【讨论】:

这不符合Map 接口。 entrySet() 的正确实现是:“返回此映射中包含的映射的集合视图。集合由映射支持,因此对映射的更改会反映在集合中,反之亦然反之亦然。" values() 也是如此。 真棒正是我要找的东西

以上是关于按值对HashMap进行排序[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在Java中按键或值对地图进行排序[重复]

HashMap按键排序和按值排序

反向排序HashMap? [复制]

如何根据值对 HashMap 的元素进行排序? [复制]

java 以价值值对的HashMap进行排序

按值对字典进行排序[重复]