Java 集合排序:比较方法违反其一般约定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28004269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Collections sort: Comparison method violates its general contract
提问by math_law
I know it has been asked and answered millions of times but still I am unable to figure out why I am receiving with the violation during sort. Here is my code:
我知道它已经被询问和回答了数百万次,但我仍然无法弄清楚为什么我在排序过程中收到了违规行为。这是我的代码:
Collections.sort(sorted, new Comparator<MyObject>() {
@Override
public int compare(MyObject m1, MyObject m2) {
// Actual energy comparison :-
// THE higher the energy, the earlier in the list
float delta = m1.getTotalEnergy() - m2.getTotalEnergy();
if (delta > 0) {
return 1;
} else if (delta < 0) {
return -1;
} else {
return 0;
}
}
});
and I receive this error
我收到这个错误
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:895)
at java.util.TimSort.mergeAt(TimSort.java:512)
at java.util.TimSort.mergeForceCollapse(TimSort.java:453)
at java.util.TimSort.sort(TimSort.java:250)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.ArrayList.sort(ArrayList.java:1454)
at java.util.Collections.sort(Collections.java:175)
Any ideas ?
有任何想法吗 ?
回答by Elliott Frisch
Assuming getTotalEnergy()
return(s) float
, you could use
假设getTotalEnergy()
return(s) float
,你可以使用
return new Float(m1.getTotalEnergy()).compareTo(m2.getTotalEnergy());
Using Float.valueOf(float)
is probably slightly more efficient, and hopefully this is easier to read.
使用Float.valueOf(float)
可能稍微更有效,希望这更容易阅读。
Float f1 = Float.valueOf(m1.getTotalEnergy());
Float f2 = Float.valueOf(m2.getTotalEnergy());
return f1.compareTo(f2);
回答by Dunes
Without reference to MyObject
. My guess is that the comparator is inconsistent with MyObject.equal
.
不参考MyObject
. 我的猜测是比较器与MyObject.equal
.
That is, the contract you are violating is:
也就是说,你违反的合同是:
(comparator.compare(mo1, mo2) == 0) == mo1.equals(mo2)
Your comparator will compare objects with the same float value as being equal, where a more complex comparator would give an ordering, whilst the equals method would say the objects were not equal. Or you could have the reverse problem -- the equals method says the objects are equal and the compare method says they are different.
您的比较器会将具有相同浮点值的对象比较为相等,其中更复杂的比较器会给出排序,而 equals 方法会说对象不相等。或者您可能会遇到相反的问题——equals 方法表示对象相等,而 compare 方法表示它们不同。
The following should work.
以下应该工作。
public int compare(MyObject m1, MyObject m2) {
if (m1 == m2) return 0;
if (m1 == null) return -1;
if (m2 == null) return 1;
if (m1.equals(m2)) return 0;
int value = Float.compare(m1.getTotalEnergy(), m2.getTotalEnergy());
if (value != 0) return value;
// Warning, this line is not fool proof as unequal objects can have identical hash
// codes.
return m1.hashCode() - m2.hashCode();
}
回答by Mr. Polywhirl
The following code has been tested with float, NaN, and null values. All cases are handled correctly. I created a hashCode()
and equals()
method to the MyObject
class.
以下代码已使用 float、NaN 和 null 值进行测试。所有案件都得到正确处理。我为类创建了一个hashCode()
和equals()
方法MyObject
。
Result: [null, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, NaN]
结果: [null, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, NaN]
SortObjectFloatProperty
SortObjectFloat 属性
package q28004269;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortObjectFloatProperty {
public static void main(String[] args) {
List<MyObject> sorted;
sorted = create(1f, 4f, 6f, null, 8f, 5f, Float.NaN, 3f, 2f, 7f, 0f, 9f);
Collections.sort(sorted, new Comparator<MyObject>() {
@Override
public int compare(MyObject m1, MyObject m2) {
if (m1 == m2) return 0;
if (m1 == null) return -1;
if (m2 == null) return 1;
if (m1.equals(m2)) return 0;
int value = Float.compare(m1.getTotalEnergy(), m2.getTotalEnergy());
if (value != 0) return value;
return m1.hashCode() - m2.hashCode();
}
});
System.out.println(sorted);
}
public static MyObject create(float totalEnergy) {
return new MyObject(totalEnergy);
}
public static List<MyObject> create(Object ...values) {
List<MyObject> objs = new ArrayList<SortObjectFloatProperty.MyObject>();
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof Float) {
objs.add(create((float) values[i]));
} else {
objs.add(null);
}
}
return objs;
}
}
MyObject
我的对象
package q28004269;
public static class MyObject {
private float totalEnergy;
public float getTotalEnergy() {
return totalEnergy;
}
public void setTotalEnergy(float totalEnergy) {
this.totalEnergy = totalEnergy;
}
public MyObject() {
this(0.0f);
}
public MyObject(float totalEnergy) {
this.totalEnergy = totalEnergy;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(totalEnergy);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MyObject other = (MyObject) obj;
return !(Float.floatToIntBits(totalEnergy) != Float.floatToIntBits(other.totalEnergy));
}
@Override
public String toString() {
return Float.toString(totalEnergy);
}
}
回答by Stuart Marks
There was some discussion of NaN
values in the comments. This is important, because NaN
violates our typical expectations of comparisons of floating point numbers. For example, Double.NaN > 0.0
and Double.NaN < 1.0
are both false!
评论中有一些关于NaN
价值观的讨论。这很重要,因为NaN
违反了我们对浮点数比较的典型期望。例如,Double.NaN > 0.0
和Double.NaN < 1.0
都是假的!
This may cause the sort to throw the exception as you encountered. Even if the exception isn't thrown, it may cause the list to end up sorted in the wrong order. Therefore, your sort comparator mustdeal with NaN
values. Fortunately, the library's built-in comparisons such as Double.compare()
do this for you. This has the same semantics as Double.compareTo()
, except that boxed Double
values aren't necessary. See the documentationfor details. Briefly, NaN
values are considered greater than all other values (including +Inf
), and negative zero is less than positive zero.
这可能会导致排序在您遇到时抛出异常。即使没有抛出异常,也可能导致列表以错误的顺序排序。因此,您的排序比较器必须处理NaN
值。幸运的是,库的内置比较Double.compare()
可以为您执行此操作。这与 具有相同的语义Double.compareTo()
,只是Double
不需要装箱的值。有关详细信息,请参阅文档。简而言之,NaN
值被认为大于所有其他值(包括+Inf
),负零小于正零。
To use Double.compare()
in a Comparator
for sort
, do this:
要Double.compare()
在Comparator
for 中使用sort
,请执行以下操作:
Collections.sort(data, new Comparator<MyObject>() {
public int compare(MyObject m1, MyObject m2) {
return Double.compare(m1.getTotalEnergy(), m2.getTotalEnergy());
}
});
If you're using Java 8, you could do this:
如果您使用的是 Java 8,则可以执行以下操作:
data.sort(Comparator.comparing(MyObject::getTotalEnergy));
To handle nulls, wrap the comparator with nullsFirst
or nullsLast
:
要处理空值,请使用nullsFirst
或包装比较器nullsLast
:
data.sort(Comparator.nullsFirst(Comparator.comparing(MyObject::getTotalEnergy)));
回答by roeygol
Maybe this will make the change:
也许这会带来改变:
public int compare(Object m1, Object m2) {
// Actual energy comparison :-
// THE higher the energy, the earlier in the list
float delta = ((MyObject)m1).getTotalEnergy() - ((MyObject)m2).getTotalEnergy();
....
}