I get this error:
Exception in thread "Thread-3" java.lang.IllegalArgumentException: Comparison method violates its general contract!
When I try to run this comparator for my entity system in Java:
private Comparator<Entity> spriteSorter = new Comparator<Entity>() {
public int compare(Entity e0, Entity e1) {
if (e1.position.getX() <= e0.position.getX())
return +1;
if (e1.position.getY() >= e0.position.getY())
return -1;
return 0;
}
};
Here is the implementation:
private void sortAndRender(Bitmap b, Vec2 offset, ArrayList<Entity> l) {
Collections.sort(l, spriteSorter);
for (int i = 0; i < l.size(); i++) {
l.get(i).render(b, offset);
}
}
This issue only really began occurring when I was displaying large amounts of entities on the screen. What is going on here?
Your comparator is just plain wrong. Better would be something like
if (e1.position.getX() != e0.position.getX())
return Integer.compare(e1.position.getX(), e0.position.getX());
if (e1.position.getY() != e0.position.getY())
return Integer.compare(e1.position.getY(), e0.position.getY());
return 0;
While #Louis beat me to it for the most part, to elaborate and possibly clarify...
Your Compare method must be fairly "stable" and complete. Yours will return 0, "equals" for a lot of cases where the X and Y are different.
I'd rewrite it as
int result = Integer.compare(e1.position.getX(), e0.position.getX());
if (result == 0)
result = Integer.compare(e1.position.getY(), e0.position.getY());
... if you have more to compare, add more if (result == 0) blah blah here...
return result;
As for "stable", let's say you have two points, a = 4,2 and b = 2,4
When you compare a to b, you get 0
But when you compare b to a, you get 1.
This is "illegal" in a comparator. a.compareTo(b) should equal -b.compareTo(a)
Haha, issue was i was for some reason moving them up on the list based on x position, and down the list based on y position??!?!? This was a really silly mistake from me
Related
I have read all thread about transitive comparator, I don't get why this comparator function is violating a rule. If someone can clean my eyes, it is quite simple I think but I cannot get it
Stack is:
java.util.TimSort.mergeLo(TimSort.java:747)
java.util.TimSort.mergeAt(TimSort.java:483)
java.util.TimSort.mergeCollapse(TimSort.java:410)
my object (simplified)
public class SleepDetails {
private DateTime time;
private SleepEnum type;
[...]
}
public enum SleepEnum {
DEEP(0), LIGHT(1), AWAKE(2), BEGIN(16), END(17);
[...]
}
The comparator a static into a class
Comparator<SleepDetails> comparator = new Comparator<SleepDetails>(){
public int compare(SleepDetails arg0, SleepDetails arg1) {
int res = arg0.getTime().compareTo(arg1.getTime());
if (res != 0)
return res;
if (arg0.getType() == arg1.getType())
return 0;
switch(arg0.getType()) {
case BEGIN:
return -1;
case END:
return 1;
default:
return 0;
}
}
};
Mainly I want to sort the events by date, in case of two events with the same datetime put begin event first and end event as last.
I don't have the collection triggering the bug
If you compare two SleepDetails instances having the same getTime(), and one of them has getType() BEGIN and the other AWAKE.
compare (one, two)
would give -1
while
compare (two, one)
would give 0
This violates the contract :
The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.
You must also check arg1.getType() in your compare method (whenever arg0.getType() is neither BEGIN nor END).
public int compare(SleepDetails arg0, SleepDetails arg1) {
int res = arg0.getTime().compareTo(arg1.getTime());
if (res != 0)
return res;
if (arg0.getType() == arg1.getType())
return 0;
switch(arg0.getType()) {
case BEGIN:
return -1;
case END:
return 1;
default:
switch(arg1.getType()) {
case BEGIN:
return 1;
case END:
return -1;
default:
return 0;
}
}
}
The problem is that your code does not distinguish enum values for the type other than BEGIN and END. In particular, it returns zero when the first type is neither BEGIN nor END, regardless of the second type.
However, this behavior is not symmetrical: if you swap the two items in a pair BEGIN and LIGHT, you would get -1 and 0, breaking the symmetry.
You can treat all types other than BEGIN and END as equal to each other, but then you need to use both sides when deciding the equality.
I have a class that implements the Comparable interface. In this class I need to override compareTo method in order to sort objects by Long values.
What I don't know is how to perform is the comparison of the Long type.
I get error when trying to check if value is greater than or less than another Long value.
I know Long is the object of long, but have no idea how to compare two Long's.
Code sample:
public int compareTo(MyEntry<K, V> object) {
if (this.value < object.value)
return -1;
if (this.value.equals(object.value))
return 0;
return 1;
}
Error message:
operator < cannot be applied to V,V
if (this.value < object.value)
^
V, V is Long, Long
Your problem is that MyEntry<K, V> doesn't tell the compiler what type of Object you're trying to compare. It doesn't know that you're comparing Long values. The best way to do this is to not worry about what type of Object you're comparing (assuming your object implements Comparable) by just using
return this.value.compareTo(object.value);
but if you want to do it manually for some reason, do this:
public int compareTo(MyEntry<K, V> object) {
if ((Long) this.value < (Long) object.value)
return -1;
if (this.value.equals(object.value))
return 0;
return 1;
}
Long l1 = new Long(3);
Long l2 = new Long(2);
return l1.compareTo(l2);
Simple no?
It would look something like this:
#Override
public int compareTo(MyEntry<K, V> object) {
if (object == null) {
throw new NullPointerException("Null parameter");
} else if (!this.getClass().equals(object.getClass())) {
throw new ClassCastException("Possible ClassLoader issue.");
} else {
return this.longValue.compareTo(object.longValue);
}
}
Coincidentally, we recently did a tutorial on comparisons in Java. Maybe it can help you.
Cast long to Long, then use compareTo method of Long.
Java is well structured, nearly all sortable Class has compareTo method.
This is a good Java practice.
#Override
public int compare(long t1, long t2) {
return Long.valueOf(t1).compareTo(t2);
}
Use longValue() method for comparison of long values.
eg:-
Long id1 = obj.getId();
Long id2 = obj1.getId();
if (id1.longValue() <= id2.longValue()) {
Sysout.......
}
assertTrue(id1.longValue() == id2.longValue())
The long compareTo command might help. The compareTo method returns an integer value to give you an answer as to whether the longs are equal, greater than, or less than each other.
Long l1 = new Long(63255);
Long l2 = new Long(71678);
int returnVal = l1.compareTo(l2);
if(returnVal > 0) {
System.out.println("l1 is greater than l2");
}
else if(returnVal < 0) {
System.out.println("l1 is less than l2");
}
else {
System.out.println("l1 is equal to l2");
}
This question already has an answer here:
Comparison method violates its general contract Exception
(1 answer)
Closed 9 years ago.
I am trying to reproduce this exception (java.lang.IllegalArgumentException: Comparison method violates its general contract!) as I need to debug a piece of code, but the code below never throws it,
try {
ArrayList al = new ArrayList();
for (int i = 1; i <= 36; i++) {
TypeAdapterSort t = new TypeAdapterSort();
t.order = i;
al.add(t);
}
System.out.println(al.size());
Collections.sort(al, new Comparator() {
public int compare(Object o1, Object o2) {
TypeAdapterSort tas1 = (TypeAdapterSort) o1;
TypeAdapterSort tas2 = (TypeAdapterSort) o2;
if (tas1.order < tas2.order)
return -1;
else
return 1;
}
});
} catch (Exception e) {
System.out.println(e);
}
Also, when I checked the JDK code it seems that this exception is thrown by Collections.sort method only when the size of the collection to be sorted is greater than 32?. What change should be made in the code block so that Collections.sort throws this exception.
This is probably your problem
if (tas1.order < tas2.order)
return -1;
else
return 1;
If the order is equal you'll get a different result depending which on goes into the Comparator first, which is not right.
The contract is something like if A < B and B < C then A < C, but in your case this can be broken depending which order the arguments get passed.
try something like
return tas1.order -tas2.order;
This also explains why you can't reproduce it as your test data never has duplicates. try adding some dupes in your test data and see if you can reproduce (before applying the fix obviously...)
If objects are equal you never return 0. The library is smart enough to detect this
I am trying to override comparable thusly:
public int compareTo(Object other) {
if(other.getlength() > this.getlength()){
return 1;
} else if (other.getlength() < this.getlength()){
return -1;
} else {
if (other.getVal() > this.getVal()){
return 1;
} else {
return -1;
}
}
}
What I want to happen, is for the list to be sorted on the length first, then if the length is the same, I want the those same lengthed items to be sorted (in place) on their values. But my implementation is not working correctly. Can anyone see what I am doing wrong?
My results are:
a b = 3
a b c = 1
a b c = 1
a b = 2
a b = 1
The results I want are:
a b c = 1
a b c = 1
a b = 3
a b = 2
a b = 1
Avoid logic where possible. Seriously - where feasible, use arithmetic to avoid if/else's. It tends to be more reliable. In this case:
public int compareTo(Object o) {
int ret = other.getlength() - this.getlength();
if ( ret == 0 ) {
ret = other.getVal() - this.getVal();
}
return ret;
}
it is not clear from your remarks that list would be already sorted or not. But you can handle that by sorting the list after comparing there lengths. But on thing which you are obviously doing wrong is object.getValue()...this doesnt makes sense you have to iterate through both lists and compare values to conclude if they are equal.
It wasnt obvious without the example sorry for above comments, It is not possible to have this result with your comparator. Your logic looks correct to me. But it would be good idea to incorporate w00t's comments also otherwise you will have a<'b as well as a>b and could cause a runtime error. Please check if the comparator is applied properly to you sorting function ( objects ).
For my data structures class our homework is to create a generic heap ADT. In the siftUp() method I need to do comparison and if the parent is smaller I need to do a swap. The problem I am having is that the comparison operators are not valid on generic types. I believe I need to use the Comparable interface but from what I read it’s not a good idea to use with Arrays. I have also search this site and I have found good information that relates to this post none of them helped me find the solution
I removed some of the code that wasn’t relevant
Thanks
public class HeapQueue<E> implements Cloneable {
private int highest;
private Integer manyItems;
private E[] data;
public HeapQueue(int a_highest) {
data = (E[]) new Object[10];
highest = a_highest;
}
public void add(E item, int priority) {
// check to see is priority value is within range
if(priority < 0 || priority > highest) {
throw new IllegalArgumentException
("Priority value is out of range: " + priority);
}
// increase the heaps capacity if array is out of space
if(manyItems == data.length)
ensureCapacity();
manyItems++;
data[manyItems - 1] = item;
siftUp(manyItems - 1);
}
private void siftUp(int nodeIndex) {
int parentIndex;
E tmp;
if (nodeIndex != 0) {
parentIndex = parent(nodeIndex);
if (data[parentIndex] < data[nodeIndex]) { <-- problem ****
tmp = data[parentIndex];
data[parentIndex] = data[nodeIndex];
data[nodeIndex] = tmp;
siftUp(parentIndex);
}
}
}
private int parent(int nodeIndex) {
return (nodeIndex - 1) / 2;
}
}
Technically you're using the comparable interface on on item, not an array. One item in the array specifically. I think the best solution here is to accept, in the constructor, a Comparator that the user can pass to compare his generic objects.
Comparator<E> comparator;
public HeapQueue(int a_highest, Comparator<E> compare)
{
this.comparator = compare;
Then, you would store that comparator in a member function and use
if (comparator.compare(data[parentIndex],data[nodeIndex]) < 0)
In place of the less than operator.
If I am reading this right, E simply needs to extend Comparable and then your problem line becomes...
if (data[parentIndex].compareTo(ata[nodeIndex]) < 0)
This is not breaking any bet-practice rules that I know of.