compareTo triggered at first add call on TreeSet in Java 1.7

I have a TreeSet with elements, and according to:

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

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

Element class have a not null safe compareTo method

I have the following code working on Java 1.5, but not in Java 1.7

  • Why I need to do a Null Safe compareTo? and why javadoc say that?
  • Why compareTo method was triggered at first add call in Java 1.7 but no in 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
        }
    }
    

    I want to understand if compareTo needs be null safe, or the problem is construct a new object with invalid data.

    This is the stacktrace:

        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)
    

    Hm. Funny. If you check line 531 of TreeMap you'll see that it compares key with itselft:

    compare(key, key);
    

    So basically it invokes

    cv.compareTo(cv);
    

    And it crushes because attr in cv is null. Class Element doesn't implement compareTo correctly. I suppose it must return 0 if you comparing object with itself itself of throwing NPE.


    TreeSet / TreeMap behaviour changed in Java 7. Consider this main method:

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

    Runs fine on 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
    

    Blows up on 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)
    
    链接地址: http://www.djcxy.com/p/76220.html

    上一篇: 重写正确的Java等于方法

    下一篇: 首先在Java 1.7中的TreeSet上添加调用compareTo