【问题标题】:Sorting HashMap by values [duplicate]按值对HashMap进行排序[重复]
【发布时间】: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;

【问题讨论】:

    标签: java sorting hashmap


    【解决方案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() 也是如此。
      • 真棒正是我要找的东西
      猜你喜欢
      • 2023-04-06
      • 2018-06-06
      • 1970-01-01
      • 2011-05-08
      • 2015-02-13
      • 2012-09-26
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      相关资源
      最近更新 更多