理解集合排序时的比较方法
Understand comparison method when sorting a collection
我有以下代码来对具有标签 属性 的项目集合进行排序:
Collections.sort(myList, new Comparator<ListItem>() {
@Override
public int compare(ListItem lhs, ListItem rhs) {
Crashlytics.log(Log.DEBUG, TAG, "lhs.getLabel(): " + lhs.getLabel() + " | rhs.getLabel(): " + rhs.getLabel());
if (lhs.getLabel() == null || rhs.getLabel() == null) {
return 0;
}
return lhs.getLabel().compareTo(rhs.getLabel());
}
});
我有一些具有以下堆栈跟踪的崩溃报告:
Caused by java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:777)
at java.util.TimSort.mergeAt(TimSort.java:514)
at java.util.TimSort.mergeForceCollapse(TimSort.java:457)
at java.util.TimSort.sort(TimSort.java:254)
at java.util.Arrays.sort(Arrays.java:1498)
at java.util.ArrayList.sort(ArrayList.java:1470)
at java.util.Collections.sort(Collections.java:201)
at uk.co.aquanetix.activities.MyActivity.onCreate(MyActivity.java:59)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
据我了解,这与比较器方法不可传递这一事实有关,如 here 所述。
我的问题是我无法重现它。它只发生在我无法弄清楚的特定情况下。
如您所见,我正在将标签值发送到 Crashlytics,因此这些是崩溃发生时发送的值。
0 | 1545921010869 | D/MyActivity lhs.getLabel(): 14:49:22 | rhs.getLabel(): 13:21:22
1 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 14:49:22
2 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 14:49:22
3 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 13:21:22
4 | 1545921010870 | D/MyActivity lhs.getLabel(): 14:53:26 | rhs.getLabel(): 13:25:41
5 | 1545921010870 | D/MyActivity lhs.getLabel(): 14:53:26 | rhs.getLabel(): 14:49:22
6 | 1545921010870 | D/MyActivity lhs.getLabel(): | rhs.getLabel(): 14:49:22
7 | 1545921010870 | D/MyActivity lhs.getLabel(): | rhs.getLabel(): 13:25:41
我用这些值构建了一个列表(加上一些带有空字符串的项目)但在我的 device/emulators.
中重现它时运气不好
我的问题是:
- 无法使用相同值重现的原因可能是什么?
- 如果没有
if
条件,代码是否会更好?
如您所述,此比较器不可传递。考虑三个 ListItem
s a
、b
和 c
,标签分别为 "a"
、"b"
和 null
。使用此比较器,compare(a, c)
和 compareTo(b, c)
都是 0
,因为 c.getLabel()
是 null
,但 compare(a, b)
显然不是,因此违反了规则传递率。
为了解决这个问题,您可以任意决定带有 null
标签的 ListItem
s 将始终放在最后(或者首先,为了论证的缘故。只需替换 1
s 在下面的代码带有 -1
s,反之亦然):
Collections.sort(myList, new Comparator<ListItem>() {
@Override
public int compare(ListItem lhs, ListItem rhs) {
if (lhs.getLabel() == null) {
if (rhs.getLabel() == null) {
return 0;
}
return 1;
}
if (rhs.getLabel() == null) {
return -1;
}
return lhs.getLabel().compareTo(rhs.getLabel());
}
});
请注意,如果您使用的是 Java 8,则可以节省大量样板代码:
myList.sort(Comparator.comparing
(ListItem::getLabel, Comparator.nullsLast(Comparator.naturalOrder())));
我有以下代码来对具有标签 属性 的项目集合进行排序:
Collections.sort(myList, new Comparator<ListItem>() {
@Override
public int compare(ListItem lhs, ListItem rhs) {
Crashlytics.log(Log.DEBUG, TAG, "lhs.getLabel(): " + lhs.getLabel() + " | rhs.getLabel(): " + rhs.getLabel());
if (lhs.getLabel() == null || rhs.getLabel() == null) {
return 0;
}
return lhs.getLabel().compareTo(rhs.getLabel());
}
});
我有一些具有以下堆栈跟踪的崩溃报告:
Caused by java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:777)
at java.util.TimSort.mergeAt(TimSort.java:514)
at java.util.TimSort.mergeForceCollapse(TimSort.java:457)
at java.util.TimSort.sort(TimSort.java:254)
at java.util.Arrays.sort(Arrays.java:1498)
at java.util.ArrayList.sort(ArrayList.java:1470)
at java.util.Collections.sort(Collections.java:201)
at uk.co.aquanetix.activities.MyActivity.onCreate(MyActivity.java:59)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
据我了解,这与比较器方法不可传递这一事实有关,如 here 所述。
我的问题是我无法重现它。它只发生在我无法弄清楚的特定情况下。
如您所见,我正在将标签值发送到 Crashlytics,因此这些是崩溃发生时发送的值。
0 | 1545921010869 | D/MyActivity lhs.getLabel(): 14:49:22 | rhs.getLabel(): 13:21:22
1 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 14:49:22
2 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 14:49:22
3 | 1545921010870 | D/MyActivity lhs.getLabel(): 13:25:41 | rhs.getLabel(): 13:21:22
4 | 1545921010870 | D/MyActivity lhs.getLabel(): 14:53:26 | rhs.getLabel(): 13:25:41
5 | 1545921010870 | D/MyActivity lhs.getLabel(): 14:53:26 | rhs.getLabel(): 14:49:22
6 | 1545921010870 | D/MyActivity lhs.getLabel(): | rhs.getLabel(): 14:49:22
7 | 1545921010870 | D/MyActivity lhs.getLabel(): | rhs.getLabel(): 13:25:41
我用这些值构建了一个列表(加上一些带有空字符串的项目)但在我的 device/emulators.
中重现它时运气不好我的问题是:
- 无法使用相同值重现的原因可能是什么?
- 如果没有
if
条件,代码是否会更好?
如您所述,此比较器不可传递。考虑三个 ListItem
s a
、b
和 c
,标签分别为 "a"
、"b"
和 null
。使用此比较器,compare(a, c)
和 compareTo(b, c)
都是 0
,因为 c.getLabel()
是 null
,但 compare(a, b)
显然不是,因此违反了规则传递率。
为了解决这个问题,您可以任意决定带有 null
标签的 ListItem
s 将始终放在最后(或者首先,为了论证的缘故。只需替换 1
s 在下面的代码带有 -1
s,反之亦然):
Collections.sort(myList, new Comparator<ListItem>() {
@Override
public int compare(ListItem lhs, ListItem rhs) {
if (lhs.getLabel() == null) {
if (rhs.getLabel() == null) {
return 0;
}
return 1;
}
if (rhs.getLabel() == null) {
return -1;
}
return lhs.getLabel().compareTo(rhs.getLabel());
}
});
请注意,如果您使用的是 Java 8,则可以节省大量样板代码:
myList.sort(Comparator.comparing
(ListItem::getLabel, Comparator.nullsLast(Comparator.naturalOrder())));