如何在Java中按键排序Map值?

454

我有一个Map,它的键和值都是字符串。

数据如下:

"question1", "1"
"question9", "1"
"question2", "4"
"question5", "2"

我想按照Map的键对其进行排序。因此,最终我将获得question1,question2,question3等。

最终,我要从这个Map中获取两个字符串:

  • 第一个字符串:问题(按顺序1..10)
  • 第二个字符串:答案(与问题的顺序相同)

现在我有以下内容:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";
}
这让我得到了一个字符串中的问题,但它们没有按顺序排列。

如果您不能使用TreeMap,在Java 8中我们可以利用toMap()方法:https://dev59.com/WnNA5IYBdhLWcg3wh-cC#40649809 - akhil_mittal
18个回答

726

简短回答

使用TreeMap。这正是它的用途。

如果这个映射被传递给您,而您无法确定类型,则可以执行以下操作:

SortedSet<String> keys = new TreeSet<>(map.keySet());
for (String key : keys) { 
   String value = map.get(key);
   // do something
}

这将按照键的自然顺序遍历地图。

更长的答案

技术上,您可以使用任何实现SortedMap的内容,但除了极少数情况外,这相当于TreeMap,就像使用Map实现通常相当于HashMap

对于键是不实现Comparable或者您不想使用自然排序的复杂类型的情况,TreeMapTreeSet有额外的构造函数,让您传递一个Comparator

// placed inline for the demonstration, but doesn't have to be a lambda expression
Comparator<Foo> comparator = (Foo o1, Foo o2) -> {
        ...
    }

SortedSet<Foo> keys = new TreeSet<>(comparator);
keys.addAll(map.keySet());

使用 TreeMapTreeSet 时要注意,它们的性能特性与 HashMapHashSet 不同。简单地说,查找或插入元素的操作将从 O(1) 变为 O(Log(N))
HashMap 中,从1000个项目增加到10000个项目并不会影响查找元素的时间,但对于 TreeMap 来说,查找时间会变慢约1.3倍(假设Log 2)。 将1000个元素移动到100,000将使每个元素查找变慢约1.6倍。

1
我正在尝试使用Treemap并根据长度对字符串键进行排序。我发现我得到的检索结果不一致。显然,这是因为TreeMap将compareTo结果为0视为“相等”?不确定如何在这种情况下使用它。 - Marc
2
compareTo() 方法返回值为0,则表示相等。如果你正在编写一个比较器,用于按字符串长度进行排序,那么你需要根据哪个字符串更长来返回正数或负数,并且只有当两个字符串长度相同时才返回0。如果a和b是字符串,则可以像这样实现:`return a.length() - b.length()'(如果要按相反的顺序排序,请交换值)。 - Jherico
大家好,如果他/她希望按键排序地图,这里的键是1、2、3、4,插入顺序是什么......为什么不使用LinkedHashSet?我们只需逐个提出问题,它就会按照插入的顺序进行排序。有人能帮我解决这个问题吗? - narancs
@Karoly LinkedHashSet可以按照插入顺序检索元素。OP想要的是按照某个预定的排序顺序检索元素,而不考虑插入顺序。 - David Berry
@cricket_007 这段代码特别演示了如何遍历一个未排序的映射表中的键。 - Jherico
1
log₂(1000)≈10,log₂(10000)≈13(四舍五入)。这只是慢了1.3倍,而不是3倍! - Walter Tross

161

假设TreeMap不适合您(并且假设您无法使用泛型):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.

3
谢谢!我需要这样做,因为我的密钥是一个复杂类型。 - Ross Hambrick
3
这只会对键列表进行排序,但不会根据键对映射本身进行排序。我还在寻找如何根据键对映射进行排序,并且可能要使用TreeMap来尝试一下:) - Crenguta S
aliciaKeys.sing(); - Aquarelle
我不知道为什么这个回答会被点赞,因为它只会对键进行排序,而不是整个 Map。即使你尝试创建一个新的 HashMap 并按排序后的键插入元素,你的新 HashMap 也无法保留插入顺序。 - Yassir Khaldi
然后,您可以使用列表的排序键从地图中获取数值 - undefined
显示剩余2条评论

79

使用 TreeMap 可以对 Map 进行排序。

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}

1
Map<String, List<String>> treeMap = new TreeMap<String, List<String>>(printHashMap); for (String str : treeMap.keySet()) { System.out.println(str + " " + treeMap.get(str)); } - vikramvi

57

只需使用TreeMap:

new TreeMap<String, String>(unsortMap);

请注意,TreeMap按其“键”的自然顺序排序。


42

我不会Java :-( 这个代码百分之百有效。比我想出来的任何可怕的解决方案都要简单得多。 - Peter Chaula

42

如果您已经有一张地图并且想按键进行排序,只需使用:

Map<String, String> treeMap = new TreeMap<String, String>(yourMap);

一个完整的工作示例:

import java.util.HashMap;
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.Iterator;

class SortOnKey {

    public static void main(String[] args) {
       HashMap<String, String> hm = new HashMap<String, String>();
       hm.put("3", "three");
       hm.put("1", "one");
       hm.put("4", "four");
       hm.put("2", "two");
       printMap(hm);
       Map<String, String> treeMap = new TreeMap<String, String>(hm);
       printMap(treeMap);
    } // main

    public static void printMap(Map<String, String> map) {
        Set s = map.entrySet();
        Iterator it = s.iterator();
        while (it.hasNext()) {
           Map.Entry entry = (Map.Entry) it.next();
           String key = (String) entry.getKey();
           String value = (String) entry.getValue();
           System.out.println(key + " => " + value);
        } // while
        System.out.println("========================");
    } // printMap

} // class

32

如果您不能使用 TreeMap,在 Java 8 中我们可以使用 Collectors 中的toMap() 方法,该方法接受以下参数:

  • keymapper: 用于生成键的映射函数
  • valuemapper: 用于生成值的映射函数
  • mergeFunction: 合并函数,用于解决与同一键相关联的值之间的冲突
  • mapSupplier: 返回一个新的、空的 Map,用于插入结果的函数。

Java 8 示例

Map<String, String> sample = new HashMap<>(); // Push some values to map
Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                    .sorted(Map.Entry.<String, String>comparingByKey().reversed())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Map<String, String> newMapSortedByValue = sample.entrySet().stream()
                        .sorted(Map.Entry.<String, String>comparingByValue().reversed())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
我们可以修改示例以使用自定义比较器并基于键进行排序,如下所示:

我们可以修改示例以使用自定义比较器并基于键进行排序,如下所示:

Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

23

使用Java 8:

Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));

20

在Java 8中

按键对Map<K,V>进行排序,将键放入List<K>

List<K> result = map.keySet().stream().sorted().collect(Collectors.toList());

对于一个 Map<K, V> 按照键进行排序,并将其放入一个 List<Map.Entry<K, V>> 中:

List<Map.Entry<K, V>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey())
       .collect(Collectors.toList());

最后但并非最不重要的: 要以区域敏感方式排序字符串,请使用Collator (比较器) 类:

Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.PRIMARY); // case insensitive collator

List<Map.Entry<String, String>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey(collator))
       .collect(Collectors.toList());

5

这段代码可以按升序和降序排序键-值映射。

<K, V extends Comparable<V>> Map<K, V> sortByValues
     (final Map<K, V> map, int ascending)
{
     Comparator<K> valueComparator =  new Comparator<K>() {
        private int ascending;
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0)
                return 1;
            else
                return ascending*compare;
        }
        public Comparator<K> setParam(int ascending)
        {
              this.ascending = ascending;
              return this;
        }
    }.setParam(ascending);

    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

作为一个例子:
Map<Integer, Double> recommWarrVals = new HashMap<Integer, Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals, -1);  // Descending order

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接