繁体   English   中英

键入TreeMap返回null

[英]Key in TreeMap returning null

所以我有一个非常奇怪的错误。 当我最初使用keySet()迭代大型TreeMap的前10个键时,我偶然发现了它。 其中一个键是返回null,就我的理解而言,这是不可能的。 所以我在下面写了测试代码:

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++;
        }

并获得以下结果:

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...

我在前十名之后切断了地图,因为那里有更多的地图,但所有这些都是一个有价值的钥匙。

所以我的问题是这样的:当我使用密钥从TreeMap直接获取(key)时,为什么我得到null,但是EntrySet返回正确的键和值?

这是我的比较器,因为我在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;
    }
  }
}

TreeMap的构建如下:

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

allMatches是HashMap<String, Integer>

TreeMap显示的迭代顺序可以看出,您使用的是自定义Comparator [否则迭代将按字典顺序排列]

请注意,根据javadocs

实现者必须确保所有x和y的sgn(compare(x,y))== -sgn(compare(y,x))。 (这意味着当且仅当compare(y,x)抛出异常时,compare(x,y)必须抛出异常。)

实现者还必须确保关系是传递的:((compare(x,y)> 0)&&(compare(y,z)> 0))表示compare(x,z)> 0。

最后,实现者必须确保compare(x,y)== 0意味着所有z的sgn(compare(x,z))== sgn(compare(y,z))。

如果您的Comparator器不应用这些规则 - 行为未定义,可能会显示奇怪的结果 - 如您所见。

编辑: [作为对编辑问题的回应]
您的隔离专区使用identity [ operator== ]来检查两个整数。
请注意, Integer是一个对象 - 因此operator==只有在它是同一个对象时才会返回true
您应该使用equals()来检查两个整数是否相同 - 甚至更好 - 使用Integer.compareTo()

你最大的问题是你在值比较器中使用==而不是.equals会破坏事物,因为不同的键被映射到具有相同intValue()不同Integer对象,这会导致更多不可预测的事情。

但是如果你修复了它,那么你的TreeMap就不会让你插入具有相同值的多个键,这几乎肯定也会导致细微的破坏。

一个更好的解决方案是这样的 ,但基本上,你应该填写地图而不按值排序,对entrySet排序,然后将条目(按顺序)复制到像LinkedHashMap这样不需要比较器的地图,但是只是按插入顺序保留条目。

可以更改比较器,这样如果值相同,它也会比较键。 这至少可以让你插入具有相同值的多个键......但它仍然是一个真正的hacky解决方案,比如上所述的基于LinkedHashMap的解决方案风险更大。

问题解决了:

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;
    }
}
}

这带来了额外的好处,即按字母顺序返回具有相同值的键。

你应该只是:

class ValueComparator implements Comparator<Integer> {


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

接下来,您需要使用比较器初始化树形图并添加所有项目:

树形图

暂无
暂无

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

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