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

Posted

技术标签:

【中文标题】如何在 Java 中按键对 Map 值进行排序?【英文标题】:How to sort Map values by key in Java? 【发布时间】:2010-10-29 16:21:41 【问题描述】:

我有一个包含键和值字符串的 Map。

数据如下:

“问题1”、“1” “问题 9”、“1” “问题 2”、“4” “问题5”,“2”

我想根据地图的键对地图进行排序。所以,最后,我将拥有question1, question2, question3....等等。

最终,我试图从这张地图中取出两个字符串。

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

现在我有以下内容:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) 
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";

这让我得到了一个字符串中的问题,但它们不是按顺序排列的。

【问题讨论】:

如果你不能使用 TreeMap,在 Java 8 中我们可以使用 toMap() 方法:***.com/a/40649809/1216775 【参考方案1】:

简答

使用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 个项目移动到 10,000 个项目并不会真正影响您查找元素的时间,但对于 TreeMap,查找时间会慢大约 3 倍(假设 Log2)。对于每个元素查找,从 1000 移动到 100,000 将慢大约 6 倍。

【讨论】:

我正在尝试使用 Treemap 并根据长度对字符串键进行排序。我发现我得到的检索结果不一致。显然是因为 TreeMap 将 0 的 compareTo 结果视为“等于”?在这种情况下不知道如何使用它。 compareTo() 结果为 0 是“等于”。如果您正在编写一个按字符串长度排序的比较器,那么您需要根据哪个字符串更长返回一个正值或负值,并且仅在两个字符串长度相同时才返回 0。如果 a 和 b 是字符串,你可以这样做 `return a.length() - b.length()' (或者如果你希望它们在另一个方向上排序,则反转值)。 大家好,如果他/她想通过键对 Map 进行排序,这里是 1,2,3,4 ,插入顺序是什么......为什么我们不使用 LinkedHashSet ?我们只是把问题一个一个地放好,它是按照插入的顺序排列的。有人可以帮我解决这个问题吗? @Karoly LinkedHashSet 可以按照您插入的顺序检索元素。 OP 想要的是以某种预定的排序顺序检索元素,而不考虑插入顺序。 @cricket_007 代码具体演示了如何遍历尚未排序的地图的键。【参考方案2】:

假设 TreeMap 对您不利(并且假设您不能使用泛型):

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

【讨论】:

谢谢!我需要做这样的事情,因为我的键是一种复杂的类型。 这只会对键列表进行排序,但不会根据键对地图本身进行排序。我也在寻找如何根据键对地图进行排序,并且可以找到一种方法。我想我将不得不尝试使用 TreeMap :) sortedKeys.sort() 如果你使用 java 8+ 会很好 aliciaKeys.sing(); 我不知道为什么会这样,这个答案只会排序键而不是地图。即使您尝试创建一个新的 HashMap 并通过排序键插入元素,您的新 HashMap 也不会保留插入顺序。【参考方案3】:

使用TreeMap您可以对地图进行排序。

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

【讨论】:

Map> treeMap = new TreeMap>(printHashMap); for (String str : treeMap.keySet()) System.out.println(str + " " + treeMap.get(str)); 【参考方案4】:

只需使用 TreeMap

new TreeMap<String, String>(unsortMap);

请注意,TreeMap 是根据其“键”的自然顺序排序的

【讨论】:

【参考方案5】:

使用TreeMap!

【讨论】:

+1 - 不够敏捷,无法击败 Jherico,Jon,但仍然相当不错。 8) 我不懂 Java :-( 这 100% 有效。比我想出的任何可怕的解决方案都简单得多【参考方案6】:

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

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

【讨论】:

【参考方案7】:

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

keymapper:生成键的映射函数 valuemapper:生成值的映射函数 mergeFunction:合并函数,用于 解决与同一键关联的值之间的冲突 ma​​pSupplier:返回一个新的空 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));

【讨论】:

【参考方案8】:

使用 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));

【讨论】:

【参考方案9】:

在 Java 8 中

要按键对Map&lt;K, V&gt; 进行排序,请将键放入List&lt;K&gt;

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

要按键排序Map&lt;K, V&gt;,将条目放入List&lt;Map.Entry&lt;K, V&gt;&gt;

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());

【讨论】:

【参考方案10】:

此代码可以按升序和降序两种顺序对键值映射进行排序。

<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

【讨论】:

【参考方案11】:
List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String str : map.keySet()) 
 list.add(str);

Collections.sort(list);
for (String str : list) 
 System.out.println(str);

【讨论】:

【参考方案12】:

在 Java 8 中,您还可以使用 .stream().sorted():

myMap.keySet().stream().sorted().forEach(key -> 
        String value = myMap.get(key);

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    
);

【讨论】:

【参考方案13】:

以防万一您不想使用TreeMap

public static Map<Integer, Integer> sortByKey(Map<Integer, Integer> map) 
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        list.sort(Comparator.comparingInt(Map.Entry::getKey));
        Map<Integer, Integer> sortedMap = new LinkedHashMap<>();
        list.forEach(e -> sortedMap.put(e.getKey(), e.getValue()));
        return sortedMap;
    

另外,如果您想根据 values 对地图进行排序,只需将 Map.Entry::getKey 更改为 Map.Entry::getValue

【讨论】:

这出现在我的评论日志中。有人试图用变量名修复一些错误(但失败了)。所以,我正确地修复了它们,但是,这个答案从根本上是错误的。向 HashMap 添加值的顺序无关紧要。地图没有顺序。 ***.com/questions/10710193/… 源代码中的e 变量是什么? (e.getKey(), e.getValue())【参考方案14】:

使用LinkedHashMap,提供密钥排序。它也提供与HashMap 相同的性能。它们都实现了Map接口,所以你只需将初始化对象HashMap替换为LinkedHashMap即可。

【讨论】:

LinkedHashMap 对我来说已经足够了,因为我已经有一个排序好的键集合并且我也按顺序插入它们,不需要实现任何 Comparable/Comparator 接口【参考方案15】:

我们也可以使用 Arrays.sort 方法对键进行排序。

Map<String, String> map = new HashMap<String, String>();
Object[] objArr = new Object[map.size()];
for (int i = 0; i < map.size(); i++) 
objArr[i] = map.get(i);

Arrays.sort(objArr);
for (Object str : objArr) 
System.out.println(str);

【讨论】:

这是按值而非键排序。【参考方案16】:

here 提供了一个很好的解决方案。我们有一个HashMap,它以未指定的顺序存储值。我们定义了一个辅助TreeMap,并使用putAll 方法将HashMap 中的所有数据复制到TreeMap 中。 TreeMap 中的结果条目按键顺序排列。

【讨论】:

【参考方案17】:

下面的树形图怎么样:

Map<String, String> sortedMap = new TreeMap<>(Comparator.comparingInt(String::length)
.thenComparing(Function.identity()));

您在这个 sortedMap 中放入的任何内容都会自动排序。 首先TreeMapMap 接口的排序实现。 有一个但是因为它在 [自然顺序时尚] [https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html] 上对键进行排序。正如 Java 文档所说,String 类型是字典自然顺序类型。想象一下下面的字符串类型的数字列表。表示下面的列表将按预期排序。

List&lt;String&gt; notSortedList = List.of("78","0", "24", "39", "4","53","32");

如果您只是使用下面的默认 TreeMap 构造函数,并像下面这样一个接一个地推送每个元素:

    Map<String, String> map = new TreeMap<>();
    for (String s : notSortedList) 
        map.put(s, s);
    


    System.out.println(map);

输出为:0=0, 14=14, 24=24, 32=32, 39=39, 4=4, 48=48, 53=53, 54=54, 78=78

如您所见,例如数字 4 在“39”之后。这是字典数据类型(如字符串)的本质。如果那个是整数数据类型,那也没关系。

要修复这个使用参数,首先检查字符串的长度,然后比较它们。在 java 8 中是这样完成的:

Map<String, String> sortedMap = new TreeMap<>(Comparator.comparingInt(String::length)
.thenComparing(Function.identity()));

它首先按长度比较每个元素,然后通过compareTo 应用检查作为与要比较的元素相同的输入。

如果你喜欢使用更容易理解的方法,上面的代码将等同于下面的代码:

   Map<String, String> sortedMap = new TreeMap<>(
             new Comparator<String>() 
                @Override
                public int compare(String o1, String o2) 
                    int lengthDifference = o1.length() - o2.length();
                    if (lengthDifference != 0) return lengthDifference;
                    return o1.compareTo(o2);
                
            
    );

因为TreeMap 构造函数接受比较器接口,您可以构建任何更复杂的复合类实现。

这也是另一种形式更简化的版本。

    Map<String,String> sortedMap = new TreeMap<>(
           (Comparator<String>) (o1, o2) ->
            
                int lengthDifference = o1.length() - o2.length();
                if (lengthDifference != 0) return lengthDifference;
                return o1.compareTo(o2);
            
    );

【讨论】:

以上是关于如何在 Java 中按键对 Map 值进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

HashMap按键排序和按值排序

c++ map基础知识、按键排序、按值排序

按键和值对地图排序

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

使用值对std :: map进行排序

Map对象在JS中排序显示