简体   繁体   English

基于键的地图排序

[英]Sorting of Map based on keys

This is not basically how to sort the HashMap based on keys. 这基本上不是基于键对HashMap进行排序的方法。 For that I could directly use TreeMap without a wink :) 为此我可以直接使用TreeMap而不用眨眼:)

What I have at the moment is 我现在拥有的是

Map<String, Object> favoritesMap = new HashMap<String, Object>();
and its contents can be
["Wednesdays" : "abcd"]
["Mondays" : "1234"]
["Not Categorized" : "pqrs"]
["Tuesdays" : "5678"]

I want to sort the HashMap based on keys and additional to this I need "Not Categorized" to be the last one to retrieve. 我想基于键对HashMap进行排序,除此之外,我需要“未分类”才能找到最后一个。

So expected while iterating over keySet is 因此,在迭代keySet时所期望的是

["Mondays", "Tuesdays", "Wednesdays", "Not Categorized"] i.e. sorted on keys and "Not Categorized" is the last one

Thought of going for HashMap while creating and at the end add ["Not Categorized" : "pqrs"] but HashMap does not guarantee the order :) 考虑在创建时加入HashMap并在最后添加["Not Categorized" : "pqrs"]HashMap不保证顺序:)

Any other pointers for the solution? 解决方案的任何其他指针?

Are you specifically excluding TreeMap for some external reason? 出于某些外部原因,您是否明确排除了TreeMap If not you could obviously use TreeMap with a specially made Comparator . 如果不是,你显然可以将TreeMap与特制的Comparator

Have you considered any of the other SortedMap s? 您是否考虑过其他任何SortedMap

If TreeMap is definitely out I would extend HashMap and make it look like there is always one more entry but that is certainly not a trivial piece of work. 如果TreeMap肯定是出局的话,我会扩展HashMap并让它看起来总是有一个条目,但这肯定不是一件小事。 You should have a very good reason not to use a SortedMap before going down this road. 在走这条路之前,你应该有一个很好的理由不使用SortedMap

Added 添加

Here is an example of how you can make a particular entry always sort to the end using a TreeMap : 下面是一个示例,说明如何使用TreeMap使特定条目始终排序到最后:

// This key should always appear at the end of the list.
public static final String AtEnd = "Always at the end";

// A sample map.
SortedMap<String, String> myMap =
        new TreeMap<>(
        new Comparator<String>() {
          @Override
          public int compare(String o1, String o2) {
            return o1.equals(AtEnd) ? 1 : o2.equals(AtEnd) ? -1 : o1.compareTo(o2);
          }
        });

private void test() {
  myMap.put("Monday", "abc");
  myMap.put("Tuesday", "def");
  myMap.put("Wednesday", "ghi");
  myMap.put(AtEnd, "XYZ");

  System.out.println("myMap: "+myMap);
  // {Monday=abc, Tuesday=def, Wednesday=ghi, Always at the end=XYZ}
}

I wonder if you are looking for some variant of that? 我想知道你是否正在寻找一些变体?

You can achieve this by using LinkedHashMap as it guarantees to return results in the order of insertion. 您可以使用LinkedHashMap来实现此目的,因为它保证按插入顺序返回结果。

Also check the following post to understand difference between map types. 另请查看以下帖子以了解地图类型之间的区别。

Difference between HashMap, LinkedHashMap and TreeMap HashMap,LinkedHashMap和TreeMap之间的区别

Or just a create a custom class which holds a different key than the value. 或者只是创建一个自定义类,其中包含与值不同的键。 Sort according to the key of that class. 根据该类的键进行排序。 For your case make the key same value as the day, and for "Not Categorized" case ensure that its key starts later than any of the other keys, for example make it "Z_Not Categorized". 对于您的情况,使密钥与日期相同,对于“未分类”的情况,确保其密钥的启动时间晚于任何其他密钥,例如使其“Z_Not Categorized”。

public ComplexKey
{
    String key;
    String value;
}

ComplexKey monday = new ComplexKey("monday", "monday");
ComplexKey notCategorized = new ComplexKey("Z_Not Categorized", "Not Categorized");

Then you can write a custom comparator which sort the values according to the key of complexKey class. 然后你可以编写一个自定义比较器,根据complexKey类的键对值进行排序。

In your case I would use a TreeMap: 在你的情况下,我会使用TreeMap:

Map<DayOfWeek, Object> favoritesMap = new TreeMap<>();

where DayOfWeek is a class you declare like: 其中DayOfWeek是一个你声明的类:

class DayOfWeek implements Comparable<DayOfWeek> {

as it's not convenient to sort days of wooks as strings. 因为把日子作为字符串排序是不方便的。

In fact, the keys are always sorted. 事实上,键总是排序。 If you output the map a couple of times, you will find that the result remains the same. 如果您多次输出地图,您会发现结果保持不变。

First I'll gossip again on hashing: 首先,我将再次谈论哈希:

The reason is hashing. 原因是散列。 Each object has hashCode() method. 每个对象都有hashCode()方法。 The hash space is like a large array which contains all the possible hash values as indices. 哈希空间就像一个大型数组,它包含所有可能的哈希值作为索引。 When a new element is inserted into a HashSet or a new pair is put into a HashMap , it is placed in the hash space according to its hash code. 当将新元素插入HashSet或将新对放入HashMap ,根据其哈希码将其放置在哈希空间中。 If two elements have the same hash code, they will be compared with equals() method, if unequal, then the new element will be placed next to it. 如果两个元素具有相同的哈希码,则将它们与equals()方法进行比较,如果不相等,则将新元素放在它旁边。

Then if you know what happens there, you can implement some code like below: 然后,如果你知道那里发生了什么,你可以实现如下代码:

import java.util.*;

class MyString {
    private String str;

    public MyString (String str) {
        this.str = str;
    }

    public String toString () {
        return str;
    }

    public boolean equals (Object obj) {
        if (obj.getClass().equals(MyString.class)) {
            return obj.toString().equals(str);
        }
        return false;
    }

    public int hashCode () {
        if (str.equalsIgnoreCase("Not Categorized")) {
            return Integer.MAX_VALUE;
        } else if (str.hashCode() == Integer.MAX_VALUE) {
            return 0;
        }
        return str.hashCode();
    }
}

public class Test {
    public static void main (String args[]) {
        Map<MyString, String> m = new HashMap<MyString, String>();
        m.put(new MyString("a"), "a");
        m.put(new MyString("c"), "c");
        m.put(new MyString("Not Categorized"), "NC");
        m.put(new MyString("b"), "b");
        Set<MyString> keys = m.keySet();
        for (MyString k : keys) {
            System.out.println(m.get(k));
        }
    }
}

The result is "Not Categorized" always comes at last. 结果是“未分类”总是最后出现。 The reason is simple: it's hash value is always the maximum of integer. 原因很简单:它的哈希值始终是整数的最大值。

The reason I create a String wrapper class is String class is final, it can't be extended. 我创建一个String包装类的原因是String类是final,它不能被扩展。 So in this way, you would have your class structure a little change, but not much. 因此,通过这种方式,您的类结构会有一些变化,但不会太多。


It is possible to use TreeMap, though it would be less efficient: 可以使用TreeMap,但效率较低:

public static void main (String args[]) {
    Map<String, String> m = new TreeMap<String, String>(new Comparator<String>() {
        public int compare (String s1, String s2) {
            if (s1.equals(s2)) {
                return 0;
            }
            if (s1.equalsIgnoreCase("Not Categorized")) {
                return 1;
            }
            if (s2.equalsIgnoreCase("Not Categorized")) {
                return -1;
            }
            if (s1.hashCode() > s2.hashCode()) {
                return 1;
            } else if (s1.hashCode() < s2.hashCode()) {
                return -1
            } else {
                return 0;
            }
        }

        public boolean equals (Object obj) {
            return false;
        }
    });
    m.put("a", "a");
    m.put("c", "c");
    m.put("Not Categorized", "NC");
    m.put("b", "b");
    Set<String> keys = m.keySet();
    for (String k : keys) {
        System.out.println(m.get(k));
    }
}

The result is the same. 结果是一样的。 It will sort all the elements, but it won't change the hashing order of other strings, it only ensures "Not Categorized" always comes to be the largest one. 它将对所有元素进行排序,但不会更改其他字符串的散列顺序,它只能确保“未分类”始终成为最大的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM