Search code examples
javacastingtreesetraw-types

Java's TreeSet behavior when trying to println the content of a raw type set


When I try to run the below code I get a runtime error: java.lang.Integer cannot be cast to java.lang.String

Set vals = new TreeSet();
vals.add(1);
vals.add("two");
System.out.println(vals); 

I tried to replace it with HashSet, HashLinkedSet, ArrayList and so on, and they all ran and printed the answer. It looks like only TreeSet fails to work!

My assumption is that this happens because a TreeSet does a comparison between the objects at runtime, which would it fail.

However, I also know that other kinds of sets (like a HashSet, for example) needs to do the same comparison (but maybe in a HashSet the comparison occurs between two hashed values? And there is no casting that fails?)

Why is this happening?


Solution

  • TreeSet stores values in sorted order, hence, it needs to compare two elements for order, not just equality. It internally uses a TreeMap to store the values, and while inserting values in the TreeMap, the map compares keys for odering. The error occurs during comparison as evident from stack trace

    Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at java.lang.String.compareTo(Unknown Source)
        at java.util.TreeMap.put(Unknown Source)
        at java.util.TreeSet.add(Unknown Source)
        at Test.main(Test.java:10)
    

    The signature of compareTo method of String class looks like below:

     public int compareTo(String anotherString)
    

    So, when a call is made to this method with int argument as in your code the set contains an int value, the runtime error occurs with respect to Class cast

    Similary, if you had added String first into set and then added Integer, as shown below:

        Set vals = new TreeSet();
        vals.add("two");
        vals.add(1);
        System.out.println(vals); 
    

    You would have seen exception from Integer class

    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
        at java.lang.Integer.compareTo(Unknown Source)
        at java.util.TreeMap.put(Unknown Source)
        at java.util.TreeSet.add(Unknown Source)
        at Test.main(Test.java:10)
    

    In case of HashSet, this issue does not occur because elements are being compared for equality.

    The signature of equals method of String class looks like:

    public boolean equals(Object anObject)
    

    Here, as you can see, the input parameter is java.lang.Object, and an integer can be passed to this function without any errors. The implementation internally uses instanceof operator to check for type compatiblity, and avoids ClassCastException

    You can say that it is difference between how compareTo and equals are implemented is resulting in this error.