2

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

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

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

元素类有一个非空安全的 compareTo 方法

我有以下代码适用于 Java 1.5,但不适用于 Java 1.7

  • 为什么我需要进行 Null Safe compareTo?为什么javadoc这么说?
  • 为什么在 Java 1.7 中第一次触发了 compareTo 方法,但在 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 是否需要为空安全,或者问题是用无效数据构造一个新对象。

这是堆栈跟踪:

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

2 回答 2

3

嗯。有趣的。如果您检查第 531 行,TreeMap您会发现它会将 key 与 selft 进行比较:

compare(key, key);

所以基本上它调用

cv.compareTo(cv);

它会崩溃,因为attrincv为空。类Element没有compareTo正确实现。我想如果您将对象与自身进行比较以抛出 NPE,它必须返回 0。

于 2013-12-02T16:36:43.783 回答
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)
于 2014-09-09T17:49:43.700 回答