compareTo首先在Java 1.7中的TreeSet上添加调用

时间:2013-12-02 16:19:12

标签: java nullpointerexception compare treeset null-check

我有一个带有元素的TreeSet,并根据:

http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo%28T%29

[ 当且仅当e1.compareTo(e2)== 0与c1的每个e1和e2的e1.equals(e2)具有相同的布尔值时,C类的自然排序被认为与equals一致。 null不是任何类的实例,并且 e.compareTo(null)应抛出NullPointerException,即使e.equals(null)返回false。 ]

元素类有非null安全compareTo方法

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

  • 为什么我需要进行Null Safe比较?为什么javadoc这么说?
  • 为什么compareTo方法是在Java 1.7中首次添加调用时触发但在1.5中没有触发?
@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是否需要为null安全,或者问题是构造一个包含无效数据的新对象。

这是堆栈跟踪:

    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)

2 个答案:

答案 0 :(得分:3)

嗯。滑稽。 如果您检查TreeMap的第531行,您会看到它将密钥与itselft进行比较:

compare(key, key);

所以基本上它会调用

cv.compareTo(cv);

它因为attr中的cv为空而粉碎。类Element未正确实现compareTo。我想如果你把对象与自己投掷NPE本身进行比较,它必须返回0。

答案 1 :(得分:1)

在Java 7中更改了TreeSet / TreeMap行为。请考虑以下主要方法:

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上运行良好:

$ 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:

$ 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)