简体   繁体   English

键入TreeMap返回null

[英]Key in TreeMap returning null

So I have a very odd bug. 所以我有一个非常奇怪的错误。 I stumbled across it when I was originally using a keySet() to iterate over the first 10 keys of a large TreeMap. 当我最初使用keySet()迭代大型TreeMap的前10个键时,我偶然发现了它。 One of the keys was returning null, which should not be possible as far as my understanding goes. 其中一个键是返回null,就我的理解而言,这是不可能的。 So I wrote the test code below: 所以我在下面写了测试代码:

int i = 0;
        for (Map.Entry<String, Integer> es : sortedMap.entrySet()){
            if (i >= 10) {
                break;
            }

            if (sortedMap.containsKey(es.getKey())){
                System.out.println(es.getKey() + ":" + sortedMap.get(es.getKey()));
            } else {
                System.out.println("Key " + es.getKey() + " does not exist, yet...");
                System.out.println("This does work: " + es.getKey() + ":" + es.getValue());
                System.out.println("This does NOT work: " + es.getKey() + ":" + sortedMap.get(es.getKey()));
            }
            i++;
        }

And get the following results: 并获得以下结果:

SOAP:967
'excerpt'::679
'type'::679
Key 'author_url': does not exist, yet...
This does work: 'author_url'::679
This does NOT work: 'author_url'::null
'date'::679
Android:437
TLS:295
message:283
server:230
monthly:215
<<<<<<<<<<<<<<<<<<<<DUMPING MAP!
{SOAP=967, 'excerpt':=679, 'type':=679, 'author_url':=679, 'date':=679, Android=437, TLS=295, message=283, server=230, monthly=215...

I cut off the map after the top ten as there is a lot more in there, but all of it is a key with a value. 我在前十名之后切断了地图,因为那里有更多的地图,但所有这些都是一个有价值的钥匙。

So my question is this: Why am I getting a null when using the key to directly get(key) from the TreeMap, but the EntrySet returns the correct key and value? 所以我的问题是这样的:当我使用密钥从TreeMap直接获取(key)时,为什么我得到null,但是EntrySet返回正确的键和值?

Here is my comparator since I am ordering on Integer: 这是我的比较器,因为我在Integer上订购:

class ValueComparator implements Comparator<Object> {

  Map<String, Integer> base;
  public ValueComparator(Map<String, Integer> base) {
      this.base = base;
  }

  public int compare(Object a, Object b) {

    if ((Integer) base.get(a) < (Integer) base.get(b)) {
      return 1;
    } else if ((Integer) base.get(a) == (Integer) base.get(b)) {
      return 0;
    } else {
      return -1;
    }
  }
}

And the TreeMap is built as following: TreeMap的构建如下:

ValueComparator bvc =  new ValueComparator(allMatches);
TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>(bvc);
//Sort the HashMap
sortedMap.putAll(allMatches);

Where allMatches is a HashMap<String, Integer> allMatches是HashMap<String, Integer>

From the order of iteration your TreeMap shows, it is definetly the case that you used a custom Comparator . TreeMap显示的迭代顺序可以看出,您使用的是自定义Comparator [Otherwise the iteration would have been in lexicographical order] [否则迭代将按字典顺序排列]

Note that according to the javadocs : 请注意,根据javadocs

The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y. 实现者必须确保所有x和y的sgn(compare(x,y))== -sgn(compare(y,x))。 (This implies that compare(x, y) must throw an exception if and only if compare(y, x) throws an exception.) (这意味着当且仅当compare(y,x)抛出异常时,compare(x,y)必须抛出异常。)

The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0. 实现者还必须确保关系是传递的:((compare(x,y)> 0)&&(compare(y,z)> 0))表示compare(x,z)> 0。

Finally, the implementer must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z. 最后,实现者必须确保compare(x,y)== 0意味着所有z的sgn(compare(x,z))== sgn(compare(y,z))。

If your Comparator does not apply these rules - the behavior is not defined, as might show strange results - as you see. 如果您的Comparator器不应用这些规则 - 行为未定义,可能会显示奇怪的结果 - 如您所见。

EDIT: [as response to the editted question] 编辑: [作为对编辑问题的回应]
Your compartor uses identity [ operator== ] to check two Integers. 您的隔离专区使用identity [ operator== ]来检查两个整数。
Note that Integer is an object - and thus operator== will return true only if it is the same object. 请注意, Integer是一个对象 - 因此operator==只有在它是同一个对象时才会返回true
You should use equals() to check if two integers are identical - or even better - use Integer.compareTo() 您应该使用equals()来检查两个整数是否相同 - 甚至更好 - 使用Integer.compareTo()

Your biggest problem is that your use of == instead of .equals in your value comparator is breaking things, because different keys are getting mapped to different Integer objects with the same intValue() , which is throwing off even more things unpredictably. 你最大的问题是你在值比较器中使用==而不是.equals会破坏事物,因为不同的键被映射到具有相同intValue()不同Integer对象,这会导致更多不可预测的事情。

But if you fixed that, then your TreeMap wouldn't let you insert multiple keys with the same value, which is almost certainly also causing subtle breakages. 但是如果你修复了它,那么你的TreeMap就不会让你插入具有相同值的多个键,这几乎肯定也会导致细微的破坏。

A better solution would be something like this , but basically, you should fill in a map without sorting by values, sort the entrySet , and then copy the entries (in order) to a map like LinkedHashMap that doesn't need a comparator, but just keeps entries in the order of insertion. 一个更好的解决方案是这样的 ,但基本上,你应该填写地图而不按值排序,对entrySet排序,然后将条目(按顺序)复制到像LinkedHashMap这样不需要比较器的地图,但是只是按插入顺序保留条目。

You might be able to change your comparator so that if the values are the same, it'll compare the keys as well. 可以更改比较器,这样如果值相同,它也会比较键。 This would at least let you insert multiple keys with the same value...but it's still a really hacky solution that's much riskier than a LinkedHashMap -based solution as described above. 这至少可以让你插入具有相同值的多个键......但它仍然是一个真正的hacky解决方案,比如上所述的基于LinkedHashMap的解决方案风险更大。

Problem solved: 问题解决了:

class ValueComparator implements Comparator<Object> {

Map<String, Integer> base;

public ValueComparator(Map<String, Integer> base) {
    this.base = base;
}

public int compare(Object a, Object b) {

    if (((Integer) base.get(a)).intValue() < ((Integer) base.get(b)).intValue()) {
        return 1;
    } else if ( ((Integer) base.get(a)).intValue() == ((Integer) base.get(b)).intValue()) {
        return ((String)a).compareTo(((String)b));
    } else {
        return -1;
    }
}
}

This comes with the additional benefit of bringing back keys with the same value in alphabetical order. 这带来了额外的好处,即按字母顺序返回具有相同值的键。

You should simply have: 你应该只是:

class ValueComparator implements Comparator<Integer> {


  public int compare(Integer a, Integer b) {
      return a.compareTo(b);
  }
}

Next you need to initialize your treemap with the comparator and add all your items: 接下来,您需要使用比较器初始化树形图并添加所有项目:

Treemap 树形图

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

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