Java comparators and comparable - java

I have only class files and no source code in java, Now I have to sort them, what shall I use Comparator or Comparable?
Appreciate your help.

Comparable is an interface implemented by classes that know how to compare themselfs with another instance of that class.
Comparator is an interface for comparing two instances of a different class.
If you have a Person class, e.g.
public class Person implements Comparable<Person> {
private final String firstName;
private final String lastName;
...
public int compareTo(Person that) {
int rv = lastName.compareTo(that.lastName);
if (rv == 0)
rv = firstName.compareTo(that.firstName);
return rv;
}
}
That is a class that has a natural sort order of by last name and then by first name, i.e. Stephen Jones comes before John Smith.
If you then wanted to sort those objects by first name and then last name, such that John Smith comes before Stephen Jones you would use a Comparator
public class PersonComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
int rv = p1.getFirstName().compareTo(p2.getFirstName());
if (rv == 0)
rv = p1.getLastName().compareTo(p2.getLastName());
return rv;
}
}
Which you use depends on what control you have over the classes and the instances.
For example, if you have a class that does not implement Comparable, but you can subclass it to implement the interface and you are sure that you will be the only person creating instances, then you can use Comparable.
In general, however, Comparator is the more flexible.

If you don't have access to source file then use Comparator to sort them.
class MyComparator implements Comparator<MyClass>{
#Override
public int compare(MyClass o1, MyClass o2) {
return o1.getType().compareTo(o2.getType());
}
}
// where getType is the getter method of the field on which you want to sort.

If possible you can extend the class and write a new class, which implements Comparable so that you can implement compareTo(). In other case you can use, Comparator as given ,
class MyComparator implements Comparator<MyClass>{
#Override
public int compare(MyClass o1, MyClass o2) {
return o1.getType().compareTo(o2.getType());
}
}
Using comparator provides you with more customization needed.

Simple Answer
Go for Comparator.
Summary of two concepts
(extracted from http://www.digizol.com/2008/07/java-sorting-comparator-vs-comparable.html)
Comparable
A comparable object is capable of comparing itself with another object. The class itself must implement the java.lang.Comparable interface in order to be able to compare its instances.
Comparator
A comparator object is capable of comparing two different objects. The class is not comparing its instances, but some other class’s instances. This comparator class must implement the java.util.Comparator interface.
Consider your existing class
Your current class is already implemented. Even if it already has a Comparable implementation, you are not aware how that comparison is implemented; hence you cannot predict your sorting. When you are not modifying the source of the class which you are trying to sort, the only option is to go with Comparator.

Related

Sorting list of objects from another Class with lambda in Java

In my taks I need to sort names with lambda. Class lambdaComparator extends class Car.
I make class LambdaComparator like this:
public class LambdaComparator extends Car
public LambdaComparator(String name) {super(name);}
In this class I need to sort objects of type Car.
In main class I have list of objects and with function I need to sort it.
In class LambdaComparator I have this:
Collections.sort(list, new Comparator<Car>() {
public int compare(Car x, Car y) {
return x.getName().compareTo(y.getName()));
}
});
How should I call function in main to get this sorted, should I make function of type void in class to somehow call it.
Edit: lambda expression
class LambdaSort<T extends Car>
private List<T> listOfCars;
public LambdaComparator(){
this.listOfCars = new ArrayList<T>();
}
public void sortCars(T cars)
listOfCars.sort((Car o1, Car o2)->o1.getName().compareTo(o2.getName());
In main function I add objects of type car to that list.
A lambda comparator would be something like this.
Comparator<Car> comp = (c1, c2)-> c1.getName().compareTo(c2.getName());
In the above example, the Comparator is comparing on a specific field of the Car class, name. Since name probably returns a string, one can use compareTo since the String class implements the Comparable (not Comparator) interface for comparing Strings.
But the lambda could be specified much more easily using one of the methods in the Comparator interface. The comparing method may take a lambda or a method reference (shown below).
Comparator<Car> comp = Comparator.comparing(Car::getName);
Then when it is passed to the sort method, the sort method will apply it to the objects under sort as comp.compare(obj1, obj2) (although it may not be called comp in the method that uses it)
For more information, check out The Java Tutorials
Take a look at what lambda is here.
If you have a list in your main class, it's as simple as just sorting it. You probably don't even need the class LambdaComparator.
List<Car> list = new ArrayList<>();
Collections.sort(list, (a, b) -> a.getName().compareTo(b.getName()));

sort a list of different type of objects

I have a list of objects which contains different types of objects but a single property is common among all.
list contains objects of Field class, Button Class, Page class etc but one property is common among all i.e. "sequence_no" &
I want to sort this list on the basis of "sequence_no".
I'd suggest creating an interface, something like "Sequenceable" with a method getSequenceNo().
public interface Sequenceable {
int getSequenceNo();
}
Your Field, Button, Page classes should implement this interface and the getSequenceNo() method will return your sequence_no.
Then you can implement your own Comparator and sort using this comparator.
For example, your comparator will look like:
class MyComparator implements Comparator<Sequenceable> {
#Override
public int compare(Sequenceable o1, Sequenceable o2) {
return o2.getSequenceNo() - o1.getSequenceNo();
}
}
Then you can sort with:
Collections.sort(list, new MyComparator());
All your objects should be exetended or inheritance by some and same super class, then the compiler will not ask you to explicitly change those objects.
The best and clean way is to have your class implementing an interface or extending a common base class .
Other way can be (which i don't prefer becpz of performance, readability and cleanliness), create a custom comparator that uses reflection which checks the
type and then compare the property
If there are a few classes you want to sort (Field,Button,Page...) you could do a Class that inherits from Comparator<Object> and use the java.lang.Object.getClass() and casting in an switch clasusule.
Something like:
public class MyComparator implements Comparator<Object>{
#Override
public int compare(Object o1, Object o2) {
int o1prop,o2prop;
switch (o1.getClass().toString()) {
case "java.Button":
((Button)o1prop).getSequence_no();
break;
default:
break;
}
switch (o2.getClass().toString()) {
case "java.Field":
((Field)o1prop).getSequence_no();
break;
default:
break;
}
return o1prop-o2prop;
}
And after that use:
Collections.sort(list, new MyComparator());

What is the difference between using comparable by passing instance of our model class to comparable and passing wrapper to comparable?

Suppose I have a Student class with attributes name and id. And I want to sort the List of students in natural order of their respective ids using Collections.sort() method. I can do it in following two ways:
public class Student implements Comparable<Student> {
private Long id;
private String name;
// getters and setters..
public int compareTo(Student student){
return this.getId().compareTo(student.getId());
}
}
And the other way is:
public class Student implements Comparable<Long> {
private Long id;
private String name;
// getters and setters..
public int compareTo(Long id){
return this.getId().compareTo(id);
}
}
And then use the Collections.sort() method to sort the list of students. My question or confusion is what difference does it make if we use one over other as they both produce the same result.
If you implement Comparable<Long>, Collections.sort() will not be able to use it on a List<Student> because Collections.sort() will have no way of figuring out where to get the Long from to pass to compareTo(Long).
Comparable's type parameter dictates by what type of objects your type is comparable with. In the former example, you're making Students comparable with each other, and in the latter example, you're making students comparable with Longs.
Generally speaking, you should use Student implements Comparable<Student>. Student implements Comparable<Long> makes little sense from an OO design point of view, since I presume you want to compare Student instances amongst each other.
EDIT: As Jason points out in another answer, Student implements Comparable<Long> is also unusable with Collections.sort(), unless Student extended Long somehow. And that's not possible because Long is final.
Also, using Student implements Comparable<Student> allows you to encapsulate the exact comparison being done. If later on, you decide to change the internal representation of id, or decide that you want to use a different field, or implement secondary comparisons using multiple fields, and so on, then existing users of your Student type won't break even if you change the compareTo(Student student) implementation.
For the above reasons you will rarely see types "in the wild" where SomeType implements Comparable<SomeOtherType>. SomeType will most likely be comparable with other instances of SomeType (i.e. SomeType implements Comparable<SomeType>).

in what way is a comparator superior to comparable?

"How will you sort collection of employee objects by its id or name". For that we can use two interfaces, i.e., Comparator and Comparable.
seems this is one of the common interview questions
But I don't see a reason why I should use both for sorting employee objects
I have been thinking on what comparator accomplishes that Comparable cannot do.
I understand that if the objects (instance variables that is compared upon) have natural ordering then comparable is the right choice.
but if custom ordering is needed (eg string length) then one could write a comparator.
my point here is comparator is only needed by the client if he wants to sort the data by some other criteria.
For example, I would implement an Employee class to sort by id using comparable interface.
but if the client wants to sort Employee objects by String(name), he would implement comparator either as a concrete class or anonymously in sorting.
Is there anything I am missing here?
For example, In the following code, for the Person object, my compareTo method, compares the age and sort it
In the compare method, I use String length (name of the person) for sorting. In theory, I could accomplish both in the compareTo method as I have implemented below.
lastly, are there any added benefits of one of the following over other
I have implemented comparator in two ways
1. as a static method which is commented out
2. as anonymous object(?) in the main method which is commented out
3. make a new class that implements comparator and call the instance of that class in collections.sort() -- this I have not done here
(The commented-out parts of the code works. They are just different implementations)
mport java.util.Collections;
import java.util.Comparator;
import java.util.*;
public class PersonComparator implements Comparable{
private String name;
private int age;
public PersonComparator(String name, int age) {
this.name = name;
this.age = age;
}
#Override
public String toString() {
return "name=" + name + ", age=" + age;
}
/*#Override
public int compareTo(Object obj) {
if (!(obj instanceof PersonComparator)) {
throw new ClassCastException("Invalid object");
}
PersonComparator p2 = (PersonComparator)obj;
return this.age-p2.age;
}*/
/*Alternative CompareTo that checks for both age and name*/
public int compareTo(Object obj) {
if (!(obj instanceof PersonComparator)) {
throw new ClassCastException("Invalid object");
}
PersonComparator p2 = (PersonComparator)obj;
if (this.age!=p2.age){
return this.age-p2.age;
}
else {
return (this.name.length()-p2.name.length());
}
}
/*public static Comparator nameLengthComparator
= new Comparator() {
#Override
public int compare(Object obj1, Object obj2) {
if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
throw new ClassCastException("Invalid object");
}
else {
PersonComparator p1 = (PersonComparator)obj1;
PersonComparator p2 = (PersonComparator)obj2;
return p1.name.length()-p2.name.length();
}
}
};*/
public static void main(String[] args){
PersonComparator p1 = new PersonComparator("Alexander", 45);
PersonComparator p2 = new PersonComparator("Pat", 27);
PersonComparator p3 = new PersonComparator("Zacky", 45);
PersonComparator p4 = new PersonComparator("Rake", 34);
List<PersonComparator> list = new ArrayList<PersonComparator>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
System.out.println("Before sorting "+ list);
Collections.sort(list);
//System.out.println("After sorting by age "+ list);
//System.out.println("Before sorting "+ list);
//Collections.sort(list, nameLengthComparator);
System.out.println("After sorting by name length "+ list);
/*Collections.sort(list, new Comparator<PersonComparator>() {
#Override
public int compare(PersonComparator p1, PersonComparator p2) {
return p1.name.length()-p2.name.length();
}
}
);*/
System.out.println("After sorting by name length "+ list);
}
}
Thanks
Comparable interface
The Comparable interface defines a type's natural ordering. Suppose you have a list of String or Integer objects; you can pass that list to
Collections.sort(list);
and you will have a sorted list. How? Because String and Integer both implement Comparable interface and the implementations of Comparable interface provide a natural ordering. Its like the class definition saying - "If you find a collection of objects of my type, order them according to the strategy I have defined in the compareTo method".
Now when you define your own type, you can define the natural ordering of the objects of your class by implementing the Comparable interface. See the Java documentation for more information on object ordering.
Comparator interface
The Comparator interface describes how to define custom strategies for object ordering. Suppose we have a simple Person type as below:
public class Person {
String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Now, by implementing the Comparator interface, you can write different strategies to order the instances of your Person type. For example, consider the two strategies for ordering Person objects given below:
class StrategyOne implements Comparator<Person> {
#Override
public int compare(Person p1, Person p2) {
return p1.getName().length() - p2.getName().length();
}
}
class StrategyTwo implements Comparator<Person> {
#Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
Here, StrategyOne will order Person objects based on the length of their names, and StrategyTwo will order Person objects based on lexicographic ordering of their names.
The ways to implement Comparator
As you can see, the concrete strategy classes are stateless, hence all instances are functionally equivalent. So, we just need a single instance of any concrete strategy class. Thus, it should be a singleton. Using anonymous classes will create a new instance each time the call is executed. Consider storing the object in a private static final field and reusing it by using static factory methods to access them [Effective Java]. For example, you can reuse the above two concrete strategies as below:
class Strategies {
private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();
private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();
public static Comparator<Person> personNameLengthComparator(){
return PERSON_NAME_LENGTH_COMPARATOR;
}
public static Comparator<Person> personNameLexicalComparator(){
return PERSON_NAME_LEXICAL_COMPARATOR;
}
}
Summary
To summarize, the Comparable interface is used to define the natural ordering of a class, and the Comparator interface is used to define particular strategies for object ordering.
In what way is a comparator superior to comparable?
It is not "superior". It is just that the two interfaces are doing (roughly) the same thing in different ways. In the Comparable case the ordering logic is in the object being ordered. In the Comparator case, the logic is in a different class from the objects being declared.
But I don't see a reason why I should use both for sorting employee objects
The only case where it would make sense to use both would be if you needed to be able to sort the objects into different orders. Then you could declare the relevant classes as implementing Comparable for the "natural" order and use Comparator objects to implement the other orders.
By the way, a comparator probably should not implement Comparable, and vice versa.
If a comparator implements Comparable that implies you are trying to order instances of the comparator object itself ...
Your PersonComparator class is misnamed. It should really be called Person.
Could you clarify one thing in your answer that we have already equals() method from Object class then why the Comparator interface is facilitating the equals() method again?
A number of points:
You still seem to be confusing the purpose of Comparable and Comparator. The equals method on a Comparator object compares the comparator with other comparators!!
The equals method tells you whether two objects are equal ... not which one comes first.
The reason that Comparator overrides equals is solely so that they can clearly document what equals(Object) does when you call it on a Comparator object. (The actual behaviour is entirely consistent with Object.equals(Object) ... but they obviously thought it necessary to do this because programmers were repeatedly getting the semantics of the method wrong.)
In what way is a comparator superior to comparable?
I won't say it is superior but one advantage is that it enables us to write multiple sort sequences. In case of Comparable, you would have to implement that interface by your class which you want to sort and you can write only one sort sequence.
With Comparator, you can make different classes for sort sequences and while sorting, you just pass the Comparator instance to COllections.sort() method.
Consider Employee class which has fields id, firstName and lastName. If you implement Comparable, you can write only one sorting logic in compareTo method.
If you implement Comparator then you can create separate sorting sequences by creating separate classes. e.g. IdSorter, FirstNameSorter and LastNameSorter which gives you way to sort Employee in multiple ways.
Read
Sorting user defined objects with Comparator
Comparable allows you to sort items in a collections based on only one field.Comparator provides the flexibility to compare items based on more than one field
For example.
class Person implements Comparable
{
int age;
String name;
Person(int age,String name)
{
this.age=age;
this.name=name;
}
public int compareTo(Object o1) // Either you can compare according to age or name
{
Person p = (Person)o1;
if (this.age==p.age)
return 0;
else if (this.age>p.age)
return 1;
else
return -1;
}
public int compareTo(Object o) //Based on name comparision
{
return (this.name.compareTo((Person)o).name));
}
public static void main (String args[])
{
List<Person> list = new ArrayList<Person>();
Person o = new Person(12,"Steve");
Person o1 = new Person(13,"Jason");
list.add(o);
list.add(o1);
Collections.sort(list);
}
}
In case of Comparable above, you can sort items either using age or name.But in case of Comparator ,you can sort the items based on more than one field.
class AgeComparison implements Comparator
{
public int compare(Object o1,Object o2)
{
Person s1 = (Person)o1;
Person s2 =(Person)o2;
if (s1.age==s2.age)
return 0;
if(s1.age>s2.age)
return 1;
else
return -1;
}
class NameComparison implements Comparator
{
public int compare(Object o1,Object o2)
{
Person s1 = (Person)o1;
Person s2 =(Person)o2;
return (s1.age.compareTo(s2.age));
}
}
To use Comparator, you have to pass the list and the instance of class you have to use.
Collections.sort(list,new NameComparison());
Collections.sort(list,new AgeComparison());
In a nutshell, the advantage of Comparator is the flexibility to sort the list based on more than one field of the object.
In general, use Comparable when the ordering is "obvious". E.g., for Strings you use alphabetical, for numbers you use numeric order. Note that a Comparable object can only implement a single compareTo() method, so you only get one option - the "natural", "obvious" option. The advantage is that it is simple and client code doesn't have to do any extra work to compare things.
Use Comparator if the ordering is less obvious, or you might want to have multiple options. For example, a Book might get sorted by Title, Author, ISBN, etc. You could have three different Comparators to handle those three cases. You might want to sort Strings by some unusual order, e.g. a special case for a foreign language, ignoring capitals, etc.
Also, if the Objects you are sorting do not implement Comparable, or you are mixing types that do not like to compare to each other (in general, this is to be avoided, but perhaps you want to be able to compare Books and Authors in a single list in some special case) you need to use Comparator.
Here you go... I have already written a lot on this clarification aided with pictures and explanations.
Please find the link below:
Comparable and Comparator
One think you can always remember and that is "they can’t be used interchangeably"
If you are using comparator , you just need to add one comparator class and pass it to Collections.sort() method along with List object no other change in existing code.
but if you implement comparable interface you will have to change code of all the model/bean classes to override compareTo() method.
so for Loose Coupling comparator is better.

sort arraylist of complex objects alphabetically

I know that Collections.sort(myArrayList) can sort an arraylist alphabetically when they are strings, but what about when they are something more complex such as a data object containing two or more variables including a String. Is there a way to sort them then?
If there isn't a way with Collections then I can imagine making a for loop or standard sorting algorithm to look at the strings variable of each object and move the object's index in the array.
But I was wondering mainly if I overlooked something about the Collections methods
Use the function taking as second parameter a Comparator.
Il allows you to pass an instance of Comparator to sort according to your needs. Note that the javadoc of Comparator contains guidelines regarding the building of comparators.
You may define the comparator as an anonymous class if it's only locally used. Here's an example where I sort objects regarding to one of their fields which is a String :
Collections.sort(groupResults, new Comparator<ProductSearchResult>() {
public int compare(ProductSearchResult result1, ProductSearchResult result2) {
return result1.product.getRsId().compareTo(result2.product.getRsId());
}
});
Alternatively, you might also make your class implement the Comparable interface but this makes sense only if you can define a natural (obvious) order.
I would create an inner class implementing the Comparator interface:
public class Car {
public double horsePower;
class CarHorsePowerComparator implements Comparator<Car> {
#Override
public int compare(Car car1, Car car2) {
return Integer.valueOf(car.horsePower).compareTo(Integer.valueOf(car2.horsePower)) }
}
}
Now when you want to sort your Car list by horsePower:
List<Car> list = new ArrayList<Car>(myCars); //your Car list
Collections.sort(list, new CarHorsePowerComparator());

Categories

Resources