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
Related
I know this has been an issue for a while now, and checked all previously answers I could get, but still this one doesn't work.
The object 'crew' represents crewmembers with ranks and other items. The comparison should be made by comparing 'assigned_rank', an int value, and if this value is equal in both instances, then 'is_trainer', a boolean, should make the difference.
This method worked great as long as it was running with java < 7. But since Java 7 I keep getting this one:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:714)
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:451)
at java.util.ComparableTimSort.mergeCollapse(ComparableTimSort.java:376)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:182)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at java.util.Collections.sort(Collections.java:155)
at dormas_flightlog.Query.getCrew(Query.java:714)
Here is the source, where some potentially dangerous parts have allready been out-commented, but it still does not work:
public class crew implements Serializable, Comparable<crew> {
private static final long serialVersionUID = 36L;
private int flightID = 0;
private int assigned_rank = 25;
private boolean is_trainer = false;
...
#Override
public int compareTo(crew him) {
int myRank = this.getAssigned_rank();
int hisRank = him.assigned_rank;
if (this == him) {
return 0;
}
if (myRank > hisRank) {
return 1;
}
if (myRank < hisRank) {
return -1;
}
if (myRank == hisRank) {
// if (is_trainer && !o.is_trainer) {
// i = 1;
// }
// if (!is_trainer && o.is_trainer) {
// i = -1;
// }
// if (is_trainer && o.is_trainer) {
// i = 0;
// }
// if (!is_trainer && !o.is_trainer) {
// i = 0;
// }
return 0;
}
return 0;
}
#Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + this.assigned_rank;
hash = 31 * hash + (this.is_trainer ? 1 : 0);
return hash;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
int myRank = this.getAssigned_rank();
int hisRank = 0;
if (o instanceof crew) {
crew him = (crew) o;
hisRank = him.assigned_rank;
} else {
return false;
}
if (myRank > hisRank) {
return false;
}
if (myRank < hisRank) {
return false;
}
if (myRank == hisRank) {
// if (is_trainer && !o.is_trainer) {
// i = 1;
// }
// if (!is_trainer && o.is_trainer) {
// i = -1;
// }
// if (is_trainer && o.is_trainer) {
// i = 0;
// }
// if (!is_trainer && !o.is_trainer) {
// i = 0;
// }
return true;
}
return false;
}
}
Implementing equals() was just a try to solve this problem. The given exception comes with or without equals(). I cannot see how the compareTo-method violates its contract. Any help is greatly appreciated....one day this code has to work with java 7 and I don't know how...
Thanks
see this:
From http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#source
Area: API: Utilities Synopsis: Updated sort behavior for Arrays and
Collections may throw an IllegalArgumentException
Description: The sorting algorithm used by java.util.Arrays.sort and
(indirectly) by java.util.Collections.sort has been replaced. The new
sort implementation may throw an IllegalArgumentException if it detects
a Comparable that violates the Comparable contract. The previous
implementation silently ignored such a situation. If the previous
behavior is desired, you can use the new system
property java.util.Arrays.useLegacyMergeSort, to restore previous
mergesort behavior.
Nature of Incompatibility: behavioral
RFE: 6804124
For more detailed info, see the bug database reference here.
maybe you just have NaN values which you compare through Collections.sort(...), this has been a problem to me and I got that exception even having right implementation of compare(obj1, obj2) method! Check that!
I was able to solve this error cause it was a bug in jdk7.
here I found the solution:
"Comparison method violates its general contract!" - TimSort and GridLayout
Basically i just had to add the
JAVA_OPTS="$JAVA_OPTS -Djava.util.Arrays.useLegacyMergeSort=true"
to my jboss
Unfortunately, none of the solutions work for Android. TimSort is used deep in Android's ViewGroup relating to addChildrenForAccessibility that shows up under Java 7 & 8. No user code is involved in any comparison.
From other reports, it is related to having RelativeLayout with overlapping items as is commonly done. For example, a TextView that appears over an Image, or two items at the same location, where you only set one visible at a time.
https://code.google.com/p/android/issues/detail?id=55933
I've not found any way around the bug. You can't set a -Djava option in Android Studio or Eclipse (at least that I could find). Forcing use of Java 1.6 should work, but doesn't. Seems like Amazon's newer Fire tablets and phones are far more sensitive to this bug than other devices.
There are rumors Java 9 will have a fix such as a run-time option that works, but with a bug that's been around for years, I have doubts it will ever be fixed - especially considering the animosity between Oracle and Google. Any yes, perhaps the bug is really deep in the Android code and should be fixed there. With more than a billion devices out there, that's not a viable solution for all the existing devices.
This question already has answers here:
"Comparison method violates its general contract!"
(13 answers)
Closed 4 years ago.
I have a priority queue that being manipulated in a while(true) loop inside a thread. In some cased I want to take the queue content and sort it in an array.
I do it this way:
Object[] array = sellQueues[0].toArray();
Arrays.sort(array);
The compare method for this priority queue is as follow:
public int compare(Order o1, Order o2) {
try {
if (o1.getBroker().getOrdersPriority() > o2.getBroker().getOrdersPriority())
return 1;
else if (o1.getBroker().getOrdersPriority() < o2.getBroker().getOrdersPriority())
return -1;
else {
if (o1.getBeginDate().before(o2.getBeginDate()))
return 1;
else if (o1.getBeginDate().after(o2.getBeginDate()))
return -1;
else {
if (o1.getBeginTime().before(o2.getBeginTime()))
return 1;
else if (o1.getBeginTime().before(o2.getBeginTime()))
return -1;
else
return 0;
}
}
} catch (NullPointerException e) {
return 0;
}
}
For some reason, Sometimes I get the error:
java.lang.IllegalArgumentException: Comparison method violates its
general contract!
The implementation in the comparator violates its general contract because:
Suppose Order A,B,C has the same broker, A has null begin date, B has begin date
1/1/2017 and C has begin date 01/01/2018. Then
A equals B and A equals C by the comparator.
B is larger than C by the comparator
Hence (2) contradict (1).
To correct the implementation, remove the catch for NullPointerException(bad habit to catch RunTimeException), add methods to compare the properties of Order, and define consistent comparison for null.
I'm facing some really strange problems while implementing a kind of Kademlia bucket in Java 8 (OpenJDK).
I need to get at least a specific number of items from so-called Buckets.
But that's not the problem.
Somehow, I get sometimes a ConcurrentModificationException while doing closest.addAll() on the ArrayList though it is just used in a single thread and I'm not iterating or doing something like that.
Do you know how to help me?
Here is my code (I know it's a mess!):
List<Neighbour> getClosest(Node n, int num) {
ArrayList<Neighbour> closest = new ArrayList<>();
int missing;
int walkDown = n.getBucket(me);
int walkUp = walkDown + 1;
boolean pleaseBreak = true;
while (true) {
missing = num - closest.size();
if (missing <= 0) {
return closest;
}
if (walkUp >= 0 && walkUp < 160) {
List<Neighbour> l = buckets[walkUp].getClosest(missing);
closest.addAll(l);
if (closest.size() >= missing) {
return closest;
}
walkUp++;
pleaseBreak = false;
}
if (walkDown >= 0 && walkDown < 160) {
List<Neighbour> l = buckets[walkDown].getClosest(missing);
closest.addAll(l);
if (closest.size() >= missing) {
return closest;
}
walkDown--;
pleaseBreak = false;
}
if (pleaseBreak) {
return closest;
}
pleaseBreak = true;
}
}
ConcurrentModificationException actually means that you are breaking the rules of iteration by somehow modifying the list while iterating it.
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
That said it is fairly clear what could be causing this issue. As closest is a new List being filled by the method it must be l that is being modified.
There are two options:
Another thread is doing it.
You already have an iterator open across the list l.
Assuming it is not 1 (or you would probably have mentioned that) I will go for:
Your getClosest method is returning a sublist of a list that is being iterated across and/or modified and addAll is also attempting to iterate over it.
To fix, make getClosest return a copy of the sublist.
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
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 ).