起初我在 TreeSet 中插入 null。之后我插入任何其他值为什么它会给出 NPE?

At first I insert null in TreeSet. After that I insert any other value why it gives NPE?

 public static void main(String []args){
    TreeSet tree = new TreeSet();
    String obj = "Ranga";
    tree.add(null);
    tree.add(obj);
 }

据我所知,TreeSet 依赖于默认的自然排序顺序。所以 JVM 内部调用了 compareTo() 方法。

在上面的例子中,情况是:

obj.compareTo(null);

那么,为什么结果是空指针异常呢?

在 "relatively recent" Java 版本中(从第 6 版开始),NullPointerException 预计会在第一个 add() 调用中抛出:

tree.add(null);

as TreeSet.add() javadoc 指出:

throw NullPointerException - if the specified element is null and this set uses natural ordering, or its comparator does not permit null elements

注意是从the JDK 6.
这样指定的 例如 JDK 5 没有明确指出这一点。

如果您使用旧的 Java 版本(如 Java 5),请注明。

从 1.7 开始,TreeSet 根本不接受 null。如果您强制添加,那么我们将得到 NullPointerException。直到 1.6 null 才被接受为第一个元素。

之前 java 7 -

对于 non-empty TreeSet,如果我们试图在 运行 时插入空值,您将得到 NullPointerException。这是因为当树中存在某些元素时,在插入任何对象之前,它会使用 compareTo() 方法将新对象与现有对象进行比较,并决定将新对象放在哪里。因此,通过插入 null,compareTo() 方法会在内部抛出 NullPointerException。

TreeMap Add method documentation

当您尝试在空 TreeSet 上添加 null 时,它最初不包含任何要比较的元素,因此它的添加没有 NPE,当您将在 TreeSet 中添加第二个元素时,TreeSet 将使用 Comparable compareTo() 方法对元素进行排序并放入 TreeSet 对象中,因此它将调用 null.compareTo() ,这无疑会导致 NPE。

TreeSet 在内部由 TreeMap 支持,之前 java 7 TreeMap put(K,V) 没有对 K(key) 的空检查,并且从 java 7 中添加了空检查TreeMap put(K.V) 方法

之前 java 7 TreeMap put mehod code does not have null check -

  public V put(K key, V value) {
        Entry<K,V> t = root;

        if (t == null) {
            incrementSize();
            root = new Entry<K,V>(key, value, null);
            return null;
       }

        while (true) {
            int cmp = compare(key, t.key);
            if (cmp == 0) {
                return t.setValue(value);
            } else if (cmp < 0) {
                if (t.left != null) {
                    t = t.left;
                } else {
                    incrementSize();
                    t.left = new Entry<K,V>(key, value, t);
                    fixAfterInsertion(t.left);
                    return null;
                }
            } else { // cmp > 0
                if (t.right != null) {
                    t = t.right;
                } else {
                    incrementSize();
                    t.right = new Entry<K,V>(key, value, t);
                    fixAfterInsertion(t.right);
                    return null;
                }
            }
        }
    }

从java 7 你可以看到对key的null检查,如果它是null就会抛出NPE。

public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        do {
            parent = t;
            cmp = cpr.compare(key, t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    else {
        if (key == null)
            throw new NullPointerException();
        Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

我希望这能让您得出正确的结论。

首先,不要使用原始类型,而是利用泛型的力量:

TreeSet<String> tree = new TreeSet<>();

关于您的问题:

TreeSet不再支持添加null.

来自文档:

public boolean add(E e)

Throws NullPointerException if the specified element is null and this set uses natural ordering, or its comparator does not permit null elements.

解决此问题的方法:

提供一个 null-safe 比较器,其中 null 元素将排在第一位:

TreeSet<String> tree = new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder()));

或提供一个 null-safe 比较器,其中 null 元素将排在最后:

TreeSet<String> tree = new TreeSet<>(Comparator.nullsLast(Comparator.naturalOrder()));

只是为了维护合同,并且在自然排序的情况下Comparable强制执行该行为。

The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.