Reputation: 43
What's the difference between:
public FlyingRabbit(){
list = new ArrayList<Sellable>();
}
public void sort(Comparator<Sellable> comp) {
Collections.sort(list, comp);
}
and:
public class CompareCategory implements Comparator<Sellable> {
@Override
public int compare(Sellable s1, Sellable s2) {
return (s1.getCategory()).compareTo(s2.getCategory());
}
}
I'm confused about why do I need to use the Comparator comp
instead of using compare
inside CompareCategory
.
Upvotes: 2
Views: 2455
Reputation: 7070
Collections.sort(List<T>)
sorts the given List
according to the natural ordering of its elements. A class can define its natural ordering by implementing the Comparable
interface. This interface provides a single method, compareTo()
, which returns:
a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
On the other hand, Collections.sort(List<T>, Comparator<T>)
orders the elements of the List
according to the given Comparator
. The element's natural ordering is ignored. This second method comes in handy when the List
's elements already exhibit a natural ordering, but we need to sort them by a different ordering.
Here's a brief example with a list of Person
. The list is first sorted by Person
's natural ordering (lastName
and name
), and then by a given Comparator
that compares only by age
.
class Person implements Comparable<Person> {
private String name;
private String lastName;
private int age;
// Defining Person's natural ordering by creating a comparator that compares by last name and name
private static final Comparator<Person> naturalOrdering = Comparator.comparing(Person::getLastName).thenComparing(Person::getName);
public Person(String name, String lastName, int age) {
this.name = name;
this.lastName = lastName;
this.age = age;
}
public String getName() {
return name;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Person o) {
return naturalOrdering.compare(this, o);
}
@Override
public String toString() {
return String.format("%s %s %d", name, lastName, age);
}
public static void main(String[] args) {
List<Person> list = new ArrayList<>(List.of(
new Person("Luke", "O'Brien", 30),
new Person("Conan", "O'Brien", 25),
new Person("John", "Doe", 50)
));
// Original unordered list
System.out.println(list);
// List ordered by Person's natural ordering (last name and name)
Collections.sort(list);
System.out.println(list);
// List ordered by custom comparator (age)
Collections.sort(list, Comparator.comparing(Person::getAge));
System.out.println(list);
}
}
Upvotes: 3
Reputation: 29028
You don't need to provide a comparator when objects contained in the list implement Comparable
interface.
That's how the purpose of this interface defined by the documention:
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's
compareTo
method is referred to as its natural comparison method.
On the other hand, comparator - is an object that is used to provide an ordering for collections of objects that don't have a natural ordering, i.e. there's no obvious specific way in which these object can be ordered, and their class doesn't implement Comparable
.
Comparators are handy when objects are required to be ordered in differently depending on the situation. For instance, you have a class Student
, and students can be sorted by their names, ids, grades, etc. And you can create a comparator for each of these cases either by creating a class implementing the interface Comparator
and overrides method compare()
(like in the code you've listed) or by making use of the static methods like Comparator.comparing()
introduced with Java 8.
With that said, interfaces Comparator
and Comparable
serve the same purpose (to facilitate comparison of object), but have different use cases.
Caution: don't mix things together by creating a comparator inside the compareTo()
method, as it's shown in another answer. It's a terrible idea. Why? Because such an implementation will create a new comparator for every invocation of this method. To sort a list of 10.000.000
elements, 10.000.000 * log(10.000.000)
comparisons will be required, with each comparison compareTo()
will be called and a new instance of comparator will be created. It'll have a negative impact on the application performance because creation of objects is costful, it requires memory allocation and then gives loads of work to the garbage collector.
In order to sort a list, you can use static methods sort()
from the Collections
utility class, that are part of the JDK since the very early version. And you could find lots code-snippets on the Internet, where Collection.sort()
is being used.
With Java 8 method List#sort() was introduced in the List
interface and you no longer need to resort to static methods from the Collections
class. Instead you can directly invoke method sort()
on the list, note that it always expects a comparator as an argument. If elements of the list implement Comparable
you should pass null
instead:
If the specified comparator is
null
then all elements in this list must implement theComparable
interface and the elements' natural ordering should be used.
myList.sort(null); // only if element of the implement Comparable
Upvotes: 0
Reputation: 377
If Sellable implements the interface Comparable you can use Collections.sort(list). Otherwise you should create own Comparator and use Collections.sort(list, comparator). Because there must be a rule by which to compare elements of the Sellable type.
Upvotes: 1