savak
savak

Reputation: 211

Why does the following code sort the List of objects?

Could you please explain why the following code compiles and prints [1, 2, 3, 4], as expected. I'm using Java 8.

List nums = Arrays.asList(4, 3, 2, 1);
Collections.sort(nums);
System.out.println(nums);

As I understand, four Integer instances is created here. Each list entry contains an Object reference to an Integer instance. Since the Object class doesn't implement the Comparable interface, then Collections.sort should throw ClassCastException or something like this because it cannot cast Object references to Comparable references.

Could you please point out what I'm missing?

Upvotes: 17

Views: 1479

Answers (5)

James_pic
James_pic

Reputation: 3307

The missing piece here is type erasure.

Java compiles to JVM bytecode, and JVM bytecode has no concept of generics. So at runtime, a List is precisely the same as a List<Comparable> or a List<Object>. If your code ever contained that information (which your code doesn't), then it is erased when your code is compiled.

This means that at runtime, there's no difference between a List<Object> and a List<Comparable>, other than the actual elements they contain (as a consequence, it also means that if your List contains no elements, then there's no way of telling what sort of List it was meant to be).

Collections.sort is able to sort any List whose elements all implement Comparable, and are comparable with each other. Since your List contains Integers, which implement Comparable, Collections.sort is able to sort it.

Perhaps confusingly, this isn't true of arrays, for historical reasons. Comparable[] and Object[] are completely different types. In principle, this means that Java's array sorting methods could refuse to sort Object[] arrays, as you might expect. In practice they do not, and Arrays.sort accepts Object[] arrays, analogously to Collections.sort.

Upvotes: 1

kai
kai

Reputation: 6887

With 1,2,3,4 you are creating int literals. While passing them to asList(T... a) they get boxed into Integer objects which implements Comparable (public final class Integer extends Number implements Comparable<Integer>), so you can sort them.

Update

Comment: Yes, but the List is declared as List, so it's the synonym to List<Object>, not List<Integer>, and Object doesn't implement Comparable.

Answer: You do not specify a generic type for the list and the Collections.sort() method only checks if the object's class extends Comparable. If the list has no type, your compiler should only give you a warning and everything should work fine since Integer's are comparable.

The source code of the sort method

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
}

Update

Execute this piece of code to see what happens if the class does not implements Comparable.

public class Test
{
    public static void main(String[] args)
    {
        List objs = new ArrayList<>();
        objs.add(new Test());
        objs.add(new Test());
        Collections.sort(objs);
    }
}

The cast to Comparable which is done in Line 290 of ComparableTimSort.class will fail!

Exception in thread "main" java.lang.ClassCastException: src.Test cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(Unknown Source)
at java.util.ComparableTimSort.sort(Unknown Source)
at java.util.ComparableTimSort.sort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at src.Test.main(Test.java:14)

Upvotes: 14

Natix
Natix

Reputation: 14257

There is a big difference between type of a reference and actual type of an object it points to.

Integer i = 42;
Object o = i;
System.out.println(i.getClass());
System.out.println(o.getClass());

Output:

class java.lang.Integer
class java.lang.Integer

Both i and o point to an object (or value) whose runtime type is always Integer. Pointing to the object using a reference of a more general type doesn't affect its properties or behaviour in any way. This is how polymorphism works in Java.

Therefore, both of these assignments work:

Comparable<Integer> c1 = i;
Comparable<Integer> c2 = (Comparable<Integer>) o;

Upvotes: 3

balaraman
balaraman

Reputation: 413

Actually it is a casting problem. Initially create a integer array and then you have converted into list. I hope this one is helpful for you.

    Integer[] number=new Integer[]{4,3,2,1};
    Collections.sort(Arrays.asList(number));
    System.out.println(Arrays.asList(number));

Upvotes: 1

Jander Nascimento
Jander Nascimento

Reputation: 21

Even though you are using native integer, the autobox will automatically convert it to java.lang.Integer (which implements comparable). http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html

Upvotes: 1

Related Questions