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.
Related
I try to write a generic class which compares two object with the same type. The class shall get me the smallest of both. My code doesn't work.
public class compare < T > {
private T type1;
private T type2;
public compare(T type1, T type2) {
this.type1 = type1;
this.type2 = type2;
}
public T getSmallest() {
if (type1 > type2) {
return type2;
} else if (type1 == type2) {
return 0;
} else {
return type1;
}
}
}
The primary issue with your code is that the comparison operators < and > are defined ONLY for primitive arithmetic types (char, byte, short, int, long, float and double). You cannot apply those operators to arbitrary reference (non-primitive) types, and generic type parameters MUST be reference types.
It appears you are trying to re-invent the Comparator and Comparable interfaces. Reference types can be "compared" for ordering (i.e. larger/smaller) only if they implement the Comparable<T> interface and provide a compareTo() method, or using a helper class that implements Comparator<T>. In the first case (implements Comparable<T>) the class itself defines the comparison in whatever terms are appropriate based on its internal state; in the second case the comparison is specified in the helper implements Comparator<T> class, based on whatever internal state is visible to the helper.
Take a look at the Object Ordering section of the Oracle Java tutorials for more detail.
To expand on above comments, you should implement Comparable for any class T that you want to be able to compare/sort with other objects of type T. The interface has only one method you must implement, the compareTo function which should return a negative, positive, or zero value. You can then use this function in your generic class for T when it implements Comparable.
https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html
You should implement the Comparable but also the getSmallest should become the compareTo(T o) method.
Check the javadoc for the interaface here: https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html
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()));
}
}
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
}
}
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 have two classes - Task (which implements Comparable) and DeadlinedTask (where DeadlinedTask extends Task). And for each of them I have written an overloaded compareTo function (each has compareTo(Task) and compareTo(DeadlinedTask)).
The idea is that I can sort normal Tasks by category, and DeadlinedTasks by deadline, but I also want all of the DeadlinedTasks to be sorted above the Tasks.
When I call Collections.sort(myListOfTasks) on a list of only Tasks (no DeadlinedTasks), everything works like a charm.
However when I have a list of both Tasks and DeadlinedTasks, the objects change order, but they are not fully sorted.
I have tried returning numbers other than 1 on the interclass compares (1, 1000, 1000000 all did the same thing). Is there any way to do this through compareTo and Collections.sort, is there a different java functionality I can use, or do I have to write my own search function (as a Comparator?)?
Task compareTo Methods:
public int compareTo(Task other){
if(this.GetCategory().compareTo(other.GetCategory())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetCategory().compareTo(other.GetCategory());
}
public int compareTo(DeadlinedTask other){
return 1;
}
DeadlinedTask compareTo Methods:
public int compareTo(Task other){
return -1;
}
public int compareTo(DeadlinedTask other){
if(this.GetDeadline().compareTo(other.GetDeadline())==0)
return this.GetName().compareTo(other.GetName());
else
return this.GetDeadline().compareTo(other.GetDeadline());
}
Thanks for any help
... or do I have to write my own search function (as a Comparator?)?
Yes. I think that's the best way.
The normal way to handle equals and compareTo is to return false (for equals) or throw ClassCastException (for compareTo) if the arguments actual type doesn't match the actual type of this.
If you try to implement equals or compareTo for subtypes, you can easily create semantic anomalies such as:
a.equals(b) and b.equals(a) returning different values, or
a.compareTo(b) and b.compareTo(a) returning inconsistent values.
Avoiding those anomalies would entail making the supertype aware of the subtype. That is a bad from a design perspective because it restricts your ability to create more subtypes in the future.
For use-cases where you need to implement a rule that orders instances of two or more different classes, a Comparator is the best solution.
Per class, only one compareTo method can be used to implement the Comparable interface. If you use Comparable without generics, then this is
public int compareTo(Object o)
If you're using generics, e.g. Comparable<Task>, then it's
public int compareTo(Task o)
Your compareTo(DeadlinedTask o) method will be ignored concerning the Comparable<Task> interface. It just "accidentally" has the same name, but it's an independent overloading.
(By the way, it's not possible to implement both Comparable<Task> and Comparable<DeadlineTask>).
So what you'll have to do instead, is change your Task.compareTo(Task o) method to use instanceof (it has to use runtime information after all). I agree with Stephen, that it would even be better to write a Comparator.
Comparable defines a natural order for all instances of a class. So if DeadlinedTask should always come before Tasks, then the compareTo method should implement it.
You should not redefine compareTo in DeadlinedTask, because this would break the contract of anti-commutativity : if (t1.compareTo(t2) > 0), then t2.compareTo(t1) < 0.
I would thus completely avoid to implement Comparable in the Task class, and use a dedicated comparator when sorting a collection of tasks. If you really want your task to implement Comparable, than you need to make its implementation depend on the existence of DeadlinedTask (which is not very OO) :
public class Task implements Comparable<Task> {
// ...
public final int compareTo(Task t) {
if (this instanceof DeadlinedTask) {
if (t instanceof DeadlinedTask) {
return ((DeadlinedTask) this).getDeadline().compareTo(((DeadlinedTask) t).getDeadline());
}
else {
return -1;
}
}
else if (t instanceof DeadlinedTask) {
return 1;
}
else {
return this.category.compareTo(t.category);
}
}
}
Note that Java uses a lower-case letter at the beginning of methods (getDeadline(), and not GetDeadline()), and that you don't need to use getters to access private properties of your own class.
In addition to what StevenC have said, if you know in advance that you will have a hierarchy of value objects, you can check whether the class of the argument of the compareTo() method is a subtype of the class of the object and if yes, reverse the comparison, so you will always have the child comparing against the parent:
public boolean compareTo(Object o) {
// check for null
boolean isSubtype = getClass().isAssignableFrom(o.getClass()) && getClass()!=o.getClass()
if (isSubtype) return -((/*cast to this type*/) o).compareTo(this);
}
This way, the comparison remains consistent and the base type does not to be aware pf each individual subtype, but just that subtypes exist.
Yes it seems a comparator is the simplest (& cleanest way)
But you can simply delegate te bulk ot the work to the compareTo(...) methods you have already written, all you really need to add is code to handle comparison between the sub and super classes:
public int Compare(Task t1, Task t2) {
if (t1 instance of DeadlinedTask && !(t2 instanceof DeadlinedTask))
return 1;
else if (t2 instance of DeadlinedTask && !(t1 instanceof DeadlinedTask))
return -1;
else
return t1.compareTo(t2);
}
but it just occured, how are you declaring the classes? do you include Comparable in the implements clause of the Task class and visa versa? if not, then perhaps when the lhs object is a Task, then only compare(Task) gets called ?? otherwise you need to have both in the implements clause ie:
class Task implements Comparable<Task>, Comparable<DeadlinedTask>
The magnitude of the value returned will not change anything, ie returning 1 and 1000000 is exactly the same, as tests are only < 0, > 0 and == 0 (this contract IS specified in the docs for the Comparator interface. I used to tell students trying to remember what return values mean, to imagine comparing ints, then we could just write:
int compare (int a, int b) { return a - b; }