Java implement PriorityQueue - when no Comparator provided - java

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
}
}

Related

Comparator works for my ArrayList but code looks wrong?

I have an ArrayList of object called Course and I'm trying to sort it in 2 ways, by courseID and course start time.
class Course implements Comparable<Course> {
private int courseID;
private String courseBeginTime;
// implement the compareTo method defined in Comparable
#Override
public int compareTo(Course course) {
if (getCourseID() > course.getCourseID()){
return 1;
} else if(getCourseID() < course.getCourseID()){
return -1;
} else {
return 0;
}
}
Then I have these comparators:
//implement the comparators
class IDSorter implements Comparator<Course> {
public int compare(Course course1, Course course2) {
return Integer.compare(course1.getCourseID(), course2.getCourseID());
}
}
class startTimeSorter implements Comparator<Course> {
public int compare(Course course1, Course course2) {
return Integer.compare(Integer.parseInt(course1.getCourseBeginTime()),
Integer.parseInt(course2.getCourseBeginTime()));
}
}
I sort them in my main method like this:
Collections.sort(courseList, new IDSorter());
Collections.sort(student.getStudentSchedule(), new StartTimeSorter());
The code works, I can get the list sorted by ID or startTime.... but I don't understand why. In the Course class the compareTo method is only comparing getCourseID.
How is the StartTimeSorter, which needs to compare courseBeginTime working then?
How can I rewrite to make more sense?
If a class implements Comparable, this is considered to be the natural ordering of this class. This ordering is used when you don't give an explicit Comparator to Collections.sort. That is why the single argument version of sort takes a List<T> where T extends Comparable<? super T>. The two argument versions take a List<T> with no restrictions on T and a Comparator<? super T>. So Course.compareTo is not used in your example.
There are two variants to Collections.sort method. One takes a single argument as a collection of Comparable objects. The other one takes two arguments: first is a collection second is a comparator. You used second variant. Hence your compareTo method is not used.
If you specify a Comparator in the Collections.sort method, it will take that into account, no matter even it the class implements Comparable. Try sorting without passing the Comparator in the sort method, you will see what you are expecting, i.e., the compareTo method kicks in.
I think your compareTo method just needs to be this:
#Override
public int compareTo(Course course) {
if (getCourseID() > course.getCourseID()){
return 1;
} else if(getCourseID() < course.getCourseID()){
return -1;
} else {
return Integer.compare(Integer.parseInt(getCourseBeginTime()),
Integer.parseInt(course.getCourseBeginTime()));
}
}

Comparing Generics

Hi I'm trying to compare two objects using by extending the generics with the interface Comparable:
public class Tree<K extends Comparable<K>>
{
Node<K> treeNode;
// Some initialization stuff
public void test(Node<K> node)
{
// Some code
// This line fails
if(node.getKey() > treeNode.getKey())
{ ... }
// Rest of the code
}
}
public interface Node<K extends Comparable<K>>
{
// Some code
public K getNode();
// Some more code
}
But some reason, the compare operator is not recognized. I thought that by extending comparable I could begin using such an operator. What am I doing wrong. Thanks.
Comparable doesn't let you use < or > to compare, just like List and Map don't let you use [] for item access. You need to call the compareTo method, which will indicate the result of the comparison by returning an integer less than, greater than, or equal to 0.
There is no operator overloading in Java. Therefore, you can't use > even if the objects implement Comparable. You have to use compareTo() instead.

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.

How to sort a list of class objects on one of its fields?

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.

Sort a Vector of custom objects

I'm trying to sort a Vector in java but my Vector is not a vector of int, it is a vector of objects
the object is:
public MyObject() {
numObj = 0;
price = new Price();
pax = new Pax();
}
so I have a Vector of MyObject and I want to order it by numObject, how do i do it, I'm new in java?
Thank you so much for all your help.
I assume you are using Collections.sort(..). You have two options:
make your class implement Comparable
when sorting, create a custom Comparator
An example for implementing Comparable would be:
public class MyObject implements Comparable<MyObject> {
// ..... other fields and methods
public int compareTo(MyObject other) {
return numObj - other.getNumObj();
}
}
This will mean the items are sorted in ascending order. If you want other, custom sorting, you can create a Comparator that defines the custom comparison and pass it as an argument to Collections.sort(..);
To sort a vector of object, first the class MyObject must implement Comparable and implement method compareTo(Object), then use Collections.sort(Vector)
class MyObject implements Comparable<MyObject> {
public int compareTo(MyObject a) {
//return either 1, 0, or -1
//that you compare between this object and object a
``}
}
//and in your logic write this line
Collections.sort(myVector);
check the JavaDoc of Vector
Implementing Comparable will work fine. However, it should be done with caution in order to prevent future bugs. The implementations above are great for sorting, but if you later decide to put your MyObject instances into a SortedSet (such as TreeSet), you will have an unexpected behavior. Items which are not identical will collide with each other if they have the same value of the numObject data member. This happens because sorted sets use the Comparable implementation (by default) in order to determine equality.
See http://eyalsch.wordpress.com/2009/11/23/comparators/ for details.

Categories

Resources