关于nullpointerexception:compareTo首先在Java 1.7中的TreeSet上添加调用时触发

compareTo triggered at first add call on TreeSet in Java 1.7

我有一个包含元素的树集,根据:

http://DOCS.Oracle .COM/JavaSe/ 7 /DOCS/API/Java/Lang/可比。

[当且仅当e1.compareto(e2)==0的布尔值与e1.equals(e2)对于类C的每个e1和e2的布尔值相同时,类C的自然顺序才被称为与equals一致。请注意,空值不是任何类的实例,并且e.compareto(null)应引发nullpointerException,即使e.equals(null)返回false。]

元素类具有非空的安全CompareTo方法

我有以下代码在Java 1.5上工作,但是在Java 1.7中没有

  • 为什么我需要做一个空安全比较?为什么JavaDoc这么说?
  • 为什么首先在Java 1.7中添加调用,但在1.5中不触发?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@Test
public void simpleTest() {
    try {
        Collection<Element> set = new TreeSet<Element>();
        Element cv = new Element(null);
        set.add(cv);//first add throws NPE (calling to compareTo())
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private class Element implements Comparable<Element> {
    private final String attr;

    public Element(String attr) {
        super();
        this.attr = attr;
    }

    @Override
    public int hashCode() {
        System.out.println("executing hashCode...");
        final int prime = 31;
        int result = 1;
        result = prime * result + getOuterType().hashCode();
        result = prime * result + ((attr == null) ? 0 : attr.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("executing equals...");
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Element other = (Element) obj;
        if (!getOuterType().equals(other.getOuterType()))
            return false;
        if (attr == null) {
            if (other.attr != null)
                return false;
        } else if (!attr.equals(other.attr))
            return false;
        return true;
    }

    private CatalogoActionTest getOuterType() {
        return CatalogoActionTest.this;
    }

    public int compareTo(Element o) {
        System.out.println("executing compareTo...");
        //throw NPE when attr is null
        return this.attr.compareTo(o.attr);//line 182
    }
}

我想了解CompareTo是否需要空安全,或者问题是用无效数据构造一个新对象。

这是stacktrace:

1
2
3
4
5
6
    java.lang.NullPointerException
    at com.MyTest$Element.compareTo(MyTest.java:182)
    at com.MyTest$Element.compareTo(MyTest.java:138)
    at java.util.TreeMap.compare(TreeMap.java:1188)
    at java.util.TreeMap.put(TreeMap.java:531)
    at java.util.TreeSet.add(TreeSet.java:255)


嗯,好笑。如果您检查EDOCX1的第531行(0),您将看到它将key与其elft进行比较:

1
compare(key, key);

所以基本上它调用

1
cv.compareTo(cv);

因为cv中的attr是空的,所以它就崩溃了。类Element没有正确实现compareTo。我想如果将对象与抛出NPE的自身进行比较,它必须返回0。


TraceSe/TreMeAP行为在Java 7中发生了变化。考虑以下主要方法:

1
2
3
4
5
6
7
8
9
10
11
import java.util.TreeSet;

public class C {

  public static void main(String[] args) {
    TreeSet<Object> ts = new TreeSet<Object>();
    ts.add(null);
    System.out.println("TreeSet size is:" + ts.size());
  }

}

在Java 6上运行良好:

1
2
3
4
5
6
$ java -showversion -cp . C
java version"1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

TreeSet size is: 1

在Java 7上爆炸:

1
2
3
4
5
6
7
8
9
10
$ java -showversion -cp . C
java version"1.7.0_55"
Java(TM) SE Runtime Environment (build 1.7.0_55-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode)

Exception in thread"main" java.lang.NullPointerException
    at java.util.TreeMap.compare(TreeMap.java:1188)
    at java.util.TreeMap.put(TreeMap.java:531)
    at java.util.TreeSet.add(TreeSet.java:255)
    at C.main(C.java:7)