Java报 Exception in thread main java.lang.IllegalArgumentException: Comparison method violates its general contract
Contents
问题
Nov 21, 2016 11:46:25 AM org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer invokeErrorHandler
WARNING: Execution of Rabbit message listener failed, and no ErrorHandler has been set.
org.springframework.amqp.rabbit.listener.ListenerExecutionFailedException: Listener method 'dequeue' threw exception
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:443)
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:344)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:546)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:472)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:58)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:107)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:608)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:454)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:471)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:455)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$300(SimpleMessageListenerContainer.java:58)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:548)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:747)
at java.util.TimSort.mergeAt(TimSort.java:483)
at java.util.TimSort.mergeCollapse(TimSort.java:410)
at java.util.TimSort.sort(TimSort.java:214)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
at com.uniweibov2.callcenter.assign.engine.impl.CcLastAssignTimePriorityAssignEngine.priorityAssign(CcLastAssignTimePriorityAssignEngine.java:44)
at com.uniweibov2.callcenter.CcAssignEngine.assignCommon(CcAssignEngine.java:119)
at com.uniweibov2.callcenter.CcAssignEngine.assign(CcAssignEngine.java:83)
at com.uniweibov2.callcenter.service.CcRouterService.newSessionRedisWithDequeue(CcRouterService.java:163)
at com.uniweibov2.callcenter.CcRouter.dequeueSessionMsg(CcRouter.java:238)
at com.uniweibov2.callcenter.mq.listener.CCSessionMsgListener.manualDequeue(CCSessionMsgListener.java:107)
at com.uniweibov2.callcenter.mq.listener.CCSessionMsgListener.dequeue(CCSessionMsgListener.java:135)
at sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273)
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:437)
... 12 more
有问题的Java代码
private static class CcLastAssignTimeComparator implements Comparator<CCUserConfig>, Serializable {
@Override
public int compare(CCUserConfig o1, CCUserConfig o2) {
long redisSizeA = o1.getLastAssignTime();
long redisSizeB = o2.getLastAssignTime();
return (int) (redisSizeA - redisSizeB);
}
}
原因
这自己写的代码,也太挫了。在公司有同事指出:强制将long
转换为int
,导致溢出。从而不满足比较器的传递性。这个异常,是在JDK1.7
及之后的版本才会抛出的.
同事的原话:
举个例子,无溢出情况下,a > b return positive, b > c return positive, 但当a - c 溢出了,a > c return negative
重现
package com.github.emacsist;
import java.util.*;
/**
* Hello world!
*/
public class App {
public static void main(String[] args) {
Random r = new Random(System.currentTimeMillis());
int n = 800;
List<Person> l = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
Person p = new Person();
if (i % 11 == 0) {
p.setAge(System.currentTimeMillis()+Integer.MAX_VALUE);
} else {
p.setAge(System.currentTimeMillis()-10 * r.nextInt(10));
}
l.add(p);
}
System.out.println("befor " + l);
Collections.sort(l, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return (int)(o1.getAge()-o2.getAge());
}
});
System.out.println("after " + l);
}
public static class Person {
private long age;
public long getAge() {
return age;
}
public void setAge(long age) {
this.age = age;
}
@Override
public String toString() {
return String.valueOf(getAge());
}
}
}
输出结果:(如果没有出现,那就执行多几次即可)
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:899)
at java.util.TimSort.mergeAt(TimSort.java:516)
at java.util.TimSort.mergeCollapse(TimSort.java:441)
at java.util.TimSort.sort(TimSort.java:245)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1454)
at java.util.Collections.sort(Collections.java:175)
at com.github.emacsist.App.main(App.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Process finished with exit code 1
解决
// ASC
Collections.sort(l, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if(o1 == null && o2 == null) {
return 0;
}
//这个根据自己的情况,null到底是在最前,还是最后
if(o1 == null) {
return -1;
}
if(o2 == null) {
return 1;
}
if(o1.getAge() > o2.getAge()) {
return 1;
}
if(o2.getAge() > o1.getAge()) {
return -1;
}
return 0;
}
});
注意
- 最好习惯返回
1,0,-1
- 如果不这样子,两个数在进行比较时,须保证是无损失精度的比较。(如果是浮点数,最好使用
Double.compare()
之类的方法来比较) - 在JDK1.7及之后版本里,必须要保证如果它们是两等的话,那应该要返回0.