Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 List.sort usage

List class in Java 8 has a new method sort added to it. Could anyone please clarify when should I prefer to use this as opposed to Collections.sort(..) method?

like image 728
Prabhjot Avatar asked Sep 30 '15 11:09

Prabhjot


3 Answers

There is no functional difference because Collections.sort(list) calls list.sort(null) and Collections.sort(list, comparator) calls list.sort(comparator).

So it's just a matter of style - when providing your own comparator, calling sort on the list is probably more natural and readable than using an external static method.

However if you just want to sort the list in natural order, Collections.sort(list) is maybe clearer than list.sort(null).

like image 102
assylias Avatar answered Oct 13 '22 14:10

assylias


You should go for List.sort.

Example of using List.sort, to sort a list of String with respect to the length of the elements:

List<String> list = new ArrayList<>(Arrays.asList("aa", "aaa", "a"));
list.sort(comparing(String::length));

Collections.sort() was the pre-Java 8 way of doing this. Note that there is no real difference between the two. If you take a look at Collections.sort source code, you can notice that it calls list.sort:

public static <T extends Comparable<? super T>> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}
like image 44
Tunaki Avatar answered Oct 13 '22 15:10

Tunaki


Though you can use the List.sort(Comparator) method, the main purpose of this method is overriding. Before Java-8 the sorting of every List was somewhat ineffective. First the list was dumped into array via toArray() method, then the array was sorted and finally the list was updated via ListIterator.set. In Java-8 this is the default behavior implemented in List.sort(Comparator) default method. However you may change it in subclasses if you can provide more efficient algorithm. Examples:

  • ArrayList and Arrays.asList() provide own sort implementation which directly sorts the internal array, thus you don't need to copy back and forth.

  • CopyOnWriteArrayList just can be sorted now! Try the following code:

    List<String> list = new CopyOnWriteArrayList<>(Arrays.asList("a", "c", "b"));
    Collections.sort(list);
    

    In Java-7 it throws UnsupportedOperationException, because supporting ListIterator.set contradicts the idea of this list (every iterator iterates the independent snapshot). In Java-8 the ListIterator.set is still unsupported, but this code works correctly due to custom sort implementation.

  • Collections.singletonList and Collections.emptyList just do nothing (sorting 0 or 1 element is a no-op) which is certainly faster than creating a one-element or zero-element array, sort it and iterate over list to set the value back.

  • Collections.unmodifiableList now just throws UnsupportedOperationException. Prior to Java-8 you got the exception only after dumping the immutable list to the array, sorting the array and starting to write it back.


Here's an example which may be useful in user code. Suppose that you want to create a mapped view of some list:

public class MappedList<T, R> extends AbstractList<R> {
    private final List<T> source;
    private final Function<? super T, ? extends R> mapper;

    public MappedList(List<T> source, Function<? super T, ? extends R> mapper) {
        this.source = source;
        this.mapper = mapper;
    }

    @Override
    public R get(int index) {
        return mapper.apply(source.get(index));
    }

    @Override
    public int size() {
        return source.size();
    }
}

Example usage:

List<String> list = Arrays.asList("a", "foo", "bb", "bar", "qq");
List<Integer> mappedList = new MappedList<>(list, String::length);
System.out.println(mappedList); // prints [1, 3, 2, 3, 2]

Works fine, but trying Collections.sort(mappedList); you will get UnsupportedOperationException (set operation does not work as you cannot reverse the mapping function). However if you implement the sort() method in MappedList, you will be able to sort it correctly (changing the original list):

@Override
public void sort(Comparator<? super R> c) {
    @SuppressWarnings("unchecked")
    Comparator<? super T> comparator = c == null ? 
        Comparator.comparing((Function<? super T, ? extends Comparable<Object>>) mapper) : 
        Comparator.comparing(mapper, c);
    source.sort(comparator);
}

List<String> list = Arrays.asList("a", "foo", "bb", "bar", "qq");
List<Integer> mappedList = new MappedList<>(list, String::length);
Collections.sort(mappedList);
System.out.println(mappedList); // prints [1, 2, 2, 3, 3]
System.out.println(list); // prints [a, bb, qq, foo, bar]
like image 34
Tagir Valeev Avatar answered Oct 13 '22 15:10

Tagir Valeev