I am trying to implement my own version of a generic PriorityQueue in Java (using binary heaps). I have chosen to use an Object array for my heap.
Object[] qArray = new Object[initial_Size];
If a user provides a comparator - the implementation is pretty straightforward as I can use the comparator's compare method when I am doing my element comparisons.
Comparator<T> comparator; //Set to a user-provided comparator in my constructor.
if(comparator.compare((T)qArray[i], (T)qArray[j])
//do something
However, the problem comes when user does not provide a default comparator. One way I could potentially handle this is to make my PriorityQueue class implement Comparator and have the comparator do the following compare -
#Override
public int compare(T o1, T o2)
{
if(this.comparator == null) //no comparator provided by user
{
return o1.toString().compareTo(o2.toString());
}
else
{
return this.comparator.compare(o1, o2);
}
}
However, this comparator is a little lame. It obviously works brilliantly for PriorityQueues of type String, but in case of (say) Integers, not quite (It would think 5 is greater than 49). Another approach would be to force the user to provide a comparator - but I know that Java Util's implementation of PriorityQueue is much kinder.
So, I tried to reverse-engineer Java's PriorityQueue a little bit and initialized a Priority Queue of a custom class type without passing in a comparator.
public class TestClass {
public class SomeClass
{
int value;
SomeClass(int value)
{
this.value = value;
}
}
public static void main(String[] args)
{
TestClass tClass = new TestClass();
TestClass.SomeClass sClass1 = tClass.new SomeClass(10);
TestClass.SomeClass sClass2 = tClass.new SomeClass(20);
PriorityQueue<TestClass.SomeClass> pQueue = new PriorityQueue<TestClass.SomeClass>();
pQueue.add(sClass1);
}
}
So this siftUpComparable method potentially holds some important clues about how the util package does its comparing, but when I tried to read the source code I was lost.
So any ideas for implementation here - wherein if a natural ordering comparator is defined on the provided object type (like Integer or String) it should be used by default.
The Exception reveals it all: Java tries to cast the given object to Comparable, if you do not provide a Comparator.
You cannot just compare objects of arbitrary types with a generic algorithm. That is exactly why you either have to provide a Comparator or make sure that your objects support the compareTo method and implement Comparable.
As you don't pass Comparator, SomeClass should implement Comparable interface i.e
public class SomeClass implements Comparable<Integer>
{
int value;
SomeClass(int value)
{
this.value = value;
}
public int compareTo(Integer o) {
....... // Logic to compare
}
}
"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.
I have a list of DataPoint objects. The class definition is:
public static class DataPoint
{
public Comparable X;
public Comparable Y;
public Comparable Z;
public String text;
...
}
"list" is an ArrayList of DataPoint objects. How do I sort list only on the X value? Would Collections.sort(list, comparator) be used here?
Yes, you should create specific comparator for each field. Example:
Comparator<DataPoint> compByX = new Comparator<DataPoint>() {
#Override
public int compare(DataPoint left, DataPoint right) {
return left.X.compareTo(right.X);
}
};
Collections.sort(list, compByX);
You have two choices:
implement Comparable<DataPoint> for your DataPoint class
write a custom comparator that implements Comparator<DataPoint> and then use Collections.sort
First solution is meaningful if you want to give a natural ordering on your objects (which will be the most used one). Usually it's the one you use first while you use comparators just when you need additional orderings.
They both behave in the same way but Comparable<T> is inherently attached to the object as it is its default comparison algorithm. Whenever sorting is involved the default one will be used unless you specify another one.
class DataPoint implements Comparable<DataPoint> {
#Override
public int compareTo(DataPoint o) {
return X.compareTo(o.X);
}
}
Mind that when you need to compare objects you usually need also other operations on them so take care of overriding hashCode() and equals(Object o). The latter is used in sorting as documentations states:
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. ... It is strongly recommended (though not required) that natural orderings be consistent with equals.
This means that if you just compare X variable then two different DataPoint objects with same X will be considered equal with respect to compareTo. This can lead to strange situations.
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());
Here is my class:
public static class __9_7_Person implements Comparator<__9_7_Person> {
private int height;
private int weight;
public __9_7_Person(int height, int weight) {
this.height = height;
this.weight = weight;
}
public int compare(__9_7_Person p1, __9_7_Person p2) {
if (p1.height != p2.height) {
return p1.height - p2.height;
}
else {
return p1.weight - p2.weight;
}
}
}
I then created an array like this:
__9_7_Person p[] = {new __9_7_Person(60, 100),
new __9_7_Person(70, 150),
new __9_7_Person(56, 90),
new __9_7_Person(75, 190),
new __9_7_Person(60, 95),
new __9_7_Person(68, 110),
};
But got exception when I called Arrays.sort(p): "Exception in thread "main" java.lang.ClassCastException: ch_9$__9_7_Person cannot be cast to java.lang.Comparable"
You implemented Comparator, not Comparable. You should implement Comparable instead of Comparator.
As stated in the docs:
All elements in the array must implement the Comparable interface.
Comparators are useful if you want to be able to have different sorting order for the same class. In this case, Comparable can't be used
You should implement Comparable for a natural ordering, in which case you don't need to pass a separator comparator into Arrays.sort. Or you could implement Comparator<__9_7_Person> (probably in a separate class, e.g. HeightWeightPersonComparator) and call:
Arrays.sort(p, new HeightWeightPersonComparator());
It's important to understand the difference between Comparable and Comparator. A Comparable implementation says "I know how to compare myself to another object of an appropriate type" - where a Comparator implementation says "I know how to compare two objects of appropriate types".
Obviously any type can only implement Comparable once (within reason), whereas there can be any number of Comparator implementations. Using a separate Comparator is more flexible unless there's an "obvious" comparison you should use. If you don't specify a Comparator, Arrays.sort will assume that each of the elements in the array can compare itself with other elements of the array, i.e. they implement Comparable.
You need to implement the java.lang.Comparable interface, not the java.util.Comparator interface.