比较器因给定的测试数据而失败 - 为什么?

Comparator fails with given test data - Why?

我的比较器有问题。它大部分时间都有效。我创建了一个失败的测试,但我看不出失败的原因或错误所在。它失败并出现以下错误:java.lang.IllegalArgumentException: Comparison method violates its general contract!

一般情况是 PDF 中的字段列表按页面排序,然后按 Y 位置排序,然后按 X 位置排序。

我希望有人能指出正确的方向,说明给定测试数据失败的原因。

import junit.framework.TestCase;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DateUtilsTest extends TestCase {
    public void testForDerrick() {
        String fields = "null\t\t27.5118\t\t496.989\n" +
        "null\t\t121.96\t\t192.52\n" +
        "0\t\t79.5814\t\t301.597\n" +
        "0\t\t79.5814\t\t264.662\n" +
        "0\t\t196.29\t\t429.681\n" +
        "0\t\t195.955\t\t314.32\n" +
        "0\t\t196.868\t\t277.982\n" +
        "0\t\t210.99\t\t429.681\n" +
        "0\t\t210.82\t\t277.982\n" +
        "0\t\t225.552\t\t429.681\n" +
        "0\t\t224.029\t\t277.982\n" +
        "0\t\t218.91\t\t198.61\n" +
        "0\t\t267.12\t\t340.05\n" +
        "0\t\t298.85\t\t346.5\n" +
        "0\t\t372.16\t\t384.81\n" +
        "null\t\t349.16\t\t346.5\n" +
        "0\t\t549.35\t\t188.459\n" +
        "0\t\t626.48\t\t457.041\n" +
        "0\t\t649.28\t\t511.764\n" +
        "0\t\t647.456\t\t503.618\n" +
        "0\t\t647.456\t\t477.432\n" +
        "0\t\t658.659\t\t539.6\n" +
        "0\t\t658.659\t\t477.432\n" +
        "0\t\t701.73\t\t474.041\n" +
        "0\t\t712.98\t\t474.041\n" +
        "0\t\t626.48\t\t121.0\n" +
        "0\t\t626.48\t\t41.0\n" +
        "0\t\t662.44\t\t117.0\n" +
        "0\t\t673.611\t\t117.0\n" +
        "0\t\t687.52\t\t117.0\n" +
        "0\t\t700.73\t\t117.0\n" +
        "0\t\t712.98\t\t117.0\n" +
        "0\t\t787.169\t\t150.654\n" +
        "null\t\t123.57\t\t532.77\n" +
        "1\t\t35.6173\t\t92.42\n" +
        "1\t\t47.83\t\t515.0\n" +
        "1\t\t47.83\t\t369.25\n" +
        "1\t\t76.5\t\t441.88\n" +
        "1\t\t76.5\t\t368.85\n" +
        "null\t\t123.57\t\t417.02\n" +
        "1\t\t99.03\t\t515.0\n" +
        "1\t\t99.03\t\t441.88\n" +
        "1\t\t99.03\t\t369.25\n" +
        "1\t\t99.03\t\t53.2\n" +
        "1\t\t109.29\t\t515.0\n" +
        "1\t\t109.29\t\t441.88\n" +
        "1\t\t109.29\t\t369.25\n" +
        "1\t\t109.29\t\t53.2\n" +
        "1\t\t119.56\t\t515.0\n" +
        "1\t\t119.56\t\t441.88\n" +
        "1\t\t119.56\t\t369.25\n" +
        "1\t\t119.56\t\t53.2\n" +
        "1\t\t129.83\t\t515.0\n" +
        "1\t\t129.83\t\t441.88\n" +
        "1\t\t129.83\t\t369.25\n" +
        "1\t\t129.83\t\t53.2\n" +
        "1\t\t140.09\t\t515.0\n" +
        "1\t\t140.09\t\t441.88\n" +
        "1\t\t140.09\t\t369.25\n" +
        "1\t\t140.09\t\t53.2\n" +
        "1\t\t150.36\t\t515.0\n" +
        "1\t\t150.36\t\t441.88\n" +
        "1\t\t150.36\t\t369.25\n" +
        "1\t\t150.36\t\t53.2\n" +
        "1\t\t160.62\t\t515.0\n" +
        "1\t\t160.62\t\t441.88\n" +
        "1\t\t160.62\t\t369.25\n" +
        "1\t\t160.62\t\t53.2\n" +
        "1\t\t171.319\t\t515.0\n" +
        "1\t\t171.319\t\t441.88\n" +
        "1\t\t171.319\t\t369.25\n" +
        "1\t\t171.319\t\t53.2\n" +
        "1\t\t180.464\t\t401.15\n" +
        "1\t\t192.194\t\t514.83\n" +
        "1\t\t192.194\t\t441.71\n" +
        "1\t\t192.194\t\t369.08\n" +
        "1\t\t202.464\t\t514.83\n" +
        "1\t\t202.464\t\t441.71\n" +
        "1\t\t202.464\t\t369.08\n" +
        "1\t\t202.464\t\t53.0295\n" +
        "1\t\t212.724\t\t514.83\n" +
        "1\t\t212.724\t\t441.71\n" +
        "1\t\t212.724\t\t369.08\n" +
        "1\t\t212.724\t\t53.0295\n" +
        "1\t\t222.994\t\t514.83\n" +
        "1\t\t222.994\t\t441.71\n" +
        "1\t\t222.994\t\t369.08\n" +
        "1\t\t222.994\t\t53.0295\n" +
        "1\t\t233.254\t\t514.83\n" +
        "1\t\t233.254\t\t441.71\n" +
        "1\t\t233.254\t\t369.08\n" +
        "1\t\t233.254\t\t53.0295\n" +
        "1\t\t243.564\t\t514.83\n" +
        "1\t\t243.564\t\t441.71\n" +
        "1\t\t243.564\t\t369.08\n" +
        "1\t\t243.564\t\t242.83\n" +
        "1\t\t243.564\t\t136.83\n" +
        "1\t\t253.874\t\t514.83\n" +
        "1\t\t253.874\t\t441.71\n" +
        "1\t\t253.874\t\t369.08\n" +
        "1\t\t253.874\t\t242.83\n" +
        "1\t\t253.874\t\t136.83\n" +
        "1\t\t264.264\t\t514.83\n" +
        "1\t\t264.264\t\t441.71\n" +
        "1\t\t264.264\t\t369.08\n" +
        "1\t\t264.264\t\t242.83\n" +
        "1\t\t264.264\t\t136.83\n" +
        "1\t\t274.154\t\t400.98\n" +
        "1\t\t285.485\t\t515.0\n" +
        "1\t\t285.485\t\t441.444\n" +
        "1\t\t285.485\t\t369.25\n" +
        "1\t\t285.485\t\t53.2\n" +
        "1\t\t295.802\t\t515.0\n" +
        "1\t\t295.802\t\t441.444\n" +
        "1\t\t295.802\t\t369.25\n" +
        "1\t\t295.802\t\t149.84\n" +
        "1\t\t295.802\t\t104.36\n" +
        "1\t\t305.576\t\t515.0\n" +
        "1\t\t305.576\t\t441.444\n" +
        "1\t\t305.576\t\t369.25\n" +
        "1\t\t305.576\t\t242.29\n" +
        "1\t\t305.576\t\t189.18\n" +
        "1\t\t305.576\t\t107.88\n" +
        "1\t\t316.153\t\t515.0\n" +
        "1\t\t316.153\t\t441.444\n" +
        "1\t\t316.153\t\t369.25\n" +
        "1\t\t316.153\t\t198.06\n" +
        "1\t\t316.153\t\t151.58\n" +
        "1\t\t326.675\t\t515.0\n" +
        "1\t\t326.675\t\t441.444\n" +
        "1\t\t326.675\t\t369.25\n" +
        "1\t\t326.675\t\t211.5\n" +
        "1\t\t326.675\t\t165.02\n" +
        "1\t\t336.47\t\t401.15\n" +
        "1\t\t347.74\t\t515.0\n" +
        "1\t\t347.74\t\t441.88\n" +
        "1\t\t347.74\t\t369.25\n" +
        "1\t\t347.74\t\t53.2\n" +
        "1\t\t358.13\t\t515.0\n" +
        "1\t\t358.13\t\t441.88\n" +
        "1\t\t358.13\t\t369.25\n" +
        "1\t\t358.13\t\t247.9\n" +
        "1\t\t358.13\t\t153.0\n" +
        "1\t\t368.02\t\t401.15\n" +
        "1\t\t400.19\t\t442.787\n" +
        "1\t\t400.19\t\t368.25\n" +
        "null\t\t123.57\t\t306.57\n" +
        "1\t\t423.977\t\t516.463\n" +
        "1\t\t423.977\t\t443.343\n" +
        "1\t\t423.977\t\t369.713\n" +
        "1\t\t423.977\t\t54.6625\n" +
        "1\t\t434.237\t\t516.463\n" +
        "1\t\t434.237\t\t443.343\n" +
        "1\t\t434.237\t\t369.713\n" +
        "1\t\t434.237\t\t54.6625\n" +
        "1\t\t444.507\t\t516.463\n" +
        "1\t\t444.507\t\t443.343\n" +
        "1\t\t444.507\t\t369.713\n" +
        "1\t\t444.507\t\t54.6625\n" +
        "1\t\t454.777\t\t516.463\n" +
        "1\t\t454.777\t\t443.343\n" +
        "1\t\t454.777\t\t369.713\n" +
        "1\t\t454.777\t\t54.6625\n" +
        "1\t\t465.037\t\t516.463\n" +
        "1\t\t465.037\t\t443.343\n" +
        "1\t\t465.037\t\t369.713\n" +
        "1\t\t465.037\t\t54.6625\n" +
        "1\t\t475.307\t\t516.463\n" +
        "1\t\t475.307\t\t443.343\n" +
        "1\t\t475.307\t\t369.713\n" +
        "1\t\t475.307\t\t54.6625\n" +
        "1\t\t485.567\t\t516.463\n" +
        "1\t\t485.567\t\t443.343\n" +
        "1\t\t485.567\t\t369.713\n" +
        "1\t\t485.567\t\t54.6625\n" +
        "1\t\t495.837\t\t516.463\n" +
        "1\t\t495.957\t\t443.343\n" +
        "1\t\t495.957\t\t369.713\n" +
        "1\t\t495.957\t\t54.6625\n" +
        "1\t\t506.847\t\t402.613\n" +
        "1\t\t517.117\t\t516.463\n" +
        "1\t\t517.117\t\t443.343\n" +
        "1\t\t517.117\t\t369.713\n" +
        "1\t\t517.117\t\t54.6625\n" +
        "1\t\t527.387\t\t516.463\n" +
        "1\t\t527.387\t\t443.343\n" +
        "1\t\t527.387\t\t369.713\n" +
        "1\t\t527.387\t\t54.6625\n" +
        "1\t\t537.647\t\t516.463\n" +
        "1\t\t537.647\t\t443.343\n" +
        "1\t\t537.647\t\t369.713\n" +
        "1\t\t537.647\t\t54.6625\n" +
        "1\t\t547.917\t\t516.463\n" +
        "1\t\t547.917\t\t443.343\n" +
        "1\t\t547.917\t\t369.713\n" +
        "1\t\t547.917\t\t54.6625\n" +
        "1\t\t558.177\t\t516.463\n" +
        "1\t\t558.177\t\t443.343\n" +
        "1\t\t558.177\t\t369.713\n" +
        "1\t\t558.177\t\t54.6625\n" +
        "1\t\t568.447\t\t516.463\n" +
        "1\t\t568.447\t\t443.343\n" +
        "1\t\t568.447\t\t369.713\n" +
        "1\t\t568.447\t\t54.6625\n" +
        "1\t\t578.707\t\t516.463\n" +
        "1\t\t578.707\t\t443.343\n" +
        "1\t\t578.707\t\t369.713\n" +
        "1\t\t578.707\t\t54.6625\n" +
        "1\t\t588.977\t\t516.463\n" +
        "1\t\t588.977\t\t443.343\n" +
        "1\t\t588.977\t\t369.713\n" +
        "1\t\t588.977\t\t54.6625\n" +
        "1\t\t599.237\t\t516.463\n" +
        "1\t\t599.237\t\t443.343\n" +
        "1\t\t599.237\t\t369.713\n" +
        "1\t\t599.237\t\t54.6625\n" +
        "1\t\t609.507\t\t516.463\n" +
        "1\t\t609.637\t\t443.343\n" +
        "1\t\t609.637\t\t369.713\n" +
        "1\t\t609.637\t\t54.6625\n" +
        "1\t\t620.527\t\t402.613\n" +
        "1\t\t630.787\t\t516.463\n" +
        "1\t\t630.787\t\t443.343\n" +
        "1\t\t630.787\t\t369.713\n" +
        "1\t\t630.787\t\t54.6625\n" +
        "1\t\t641.057\t\t516.463\n" +
        "1\t\t641.057\t\t443.343\n" +
        "1\t\t641.057\t\t369.713\n" +
        "1\t\t641.057\t\t54.6625\n" +
        "1\t\t651.317\t\t516.463\n" +
        "1\t\t651.317\t\t443.343\n" +
        "1\t\t651.317\t\t369.713\n" +
        "1\t\t651.317\t\t54.6625\n" +
        "1\t\t661.587\t\t516.463\n" +
        "1\t\t661.587\t\t443.343\n" +
        "1\t\t661.587\t\t369.713\n" +
        "1\t\t661.587\t\t54.6625\n" +
        "1\t\t671.847\t\t516.463\n" +
        "1\t\t671.847\t\t443.343\n" +
        "1\t\t671.847\t\t369.713\n" +
        "1\t\t671.847\t\t54.6625\n" +
        "1\t\t682.117\t\t516.463\n" +
        "1\t\t682.117\t\t443.343\n" +
        "1\t\t682.117\t\t369.713\n" +
        "1\t\t682.117\t\t54.6625\n" +
        "1\t\t692.387\t\t516.463\n" +
        "1\t\t692.387\t\t443.343\n" +
        "1\t\t692.387\t\t369.713\n" +
        "1\t\t692.387\t\t54.6625\n" +
        "1\t\t702.777\t\t516.463\n" +
        "1\t\t702.777\t\t443.343\n" +
        "1\t\t702.777\t\t369.713\n" +
        "1\t\t702.777\t\t54.6625\n" +
        "1\t\t712.667\t\t402.613\n" +
        "2\t\t131.82\t\t494.835\n" +
        "2\t\t166.95\t\t334.15\n" +
        "2\t\t166.95\t\t311.9\n" +
        "2\t\t180.85\t\t334.15\n" +
        "2\t\t180.85\t\t311.9\n" +
        "2\t\t193.091\t\t334.15\n" +
        "2\t\t204.861\t\t334.15\n" +
        "2\t\t198.31\t\t311.9\n" +
        "2\t\t216.78\t\t334.15\n" +
        "2\t\t216.78\t\t311.9\n" +
        "2\t\t140.206\t\t286.44\n" +
        "2\t\t140.206\t\t250.77\n" +
        "null\t\t90.11\t\t192.52\n" +
        "null\t\t47.83\t\t440.17\n" +
        "null\t\t572.54\t\t197.55\n" +
        "2\t\t140.206\t\t224.243\n" +
        "2\t\t140.206\t\t188.434\n" +
        "2\t\t152.411\t\t190.822\n" +
        "2\t\t166.95\t\t191.0\n" +
        "2\t\t198.31\t\t191.0\n" +
        "2\t\t216.78\t\t191.0\n" +
        "null\t\t166.95\t\t254.0\n" +
        "2\t\t313.6\t\t501.8\n" +
        "2\t\t313.6\t\t57.8\n" +
        "2\t\t342.27\t\t501.8\n" +
        "2\t\t342.27\t\t57.8\n" +
        "2\t\t371.07\t\t501.8\n" +
        "2\t\t371.07\t\t57.8\n" +
        "2\t\t399.87\t\t501.8\n" +
        "2\t\t399.87\t\t57.8\n" +
        "2\t\t428.67\t\t501.8\n" +
        "2\t\t428.67\t\t57.8\n" +
        "2\t\t457.47\t\t501.8\n" +
        "2\t\t457.47\t\t57.8\n" +
        "2\t\t486.27\t\t501.8\n" +
        "2\t\t486.27\t\t57.8\n" +
        "2\t\t515.07\t\t501.8\n" +
        "2\t\t515.07\t\t57.8\n" +
        "2\t\t543.87\t\t501.8\n" +
        "2\t\t543.87\t\t57.8\n" +
        "2\t\t572.67\t\t501.8\n" +
        "2\t\t572.67\t\t57.8\n" +
        "2\t\t601.47\t\t501.8\n" +
        "2\t\t601.47\t\t57.8\n" +
        "2\t\t630.27\t\t501.8\n" +
        "2\t\t630.27\t\t57.8\n" +
        "2\t\t659.07\t\t501.8\n" +
        "2\t\t659.07\t\t57.8\n" +
        "2\t\t687.87\t\t501.8\n" +
        "2\t\t687.87\t\t57.8\n" +
        "2\t\t715.92\t\t501.8\n" +
        "2\t\t715.92\t\t57.8\n" +
        "3\t\t359.57\t\t394.05\n" +
        "3\t\t390.22\t\t394.05\n" +
        "3\t\t458.086\t\t545.206\n" +
        "3\t\t458.086\t\t478.15\n" +
        "3\t\t458.086\t\t317.306\n" +
        "3\t\t488.85\t\t394.05\n" +
        "3\t\t518.65\t\t394.05\n" +
        "3\t\t538.809\t\t435.303\n" +
        "3\t\t550.477\t\t435.303\n" +
        "3\t\t568.55\t\t394.05\n" +
        "3\t\t581.219\t\t436.03\n" +
        "3\t\t590.989\t\t436.03\n" +
        "3\t\t603.112\t\t436.03\n" +
        "3\t\t620.3\t\t394.05\n" +
        "3\t\t687.503\t\t317.306\n" +
        "null\t\t639.63\t\t117.0\n" +
        "null\t\t650.984\t\t117.0\n" +
        "3\t\t314.184\t\t39.7711\n" +
        "3\t\t336.086\t\t39.7711\n" +
        "3\t\t358.272\t\t39.7711\n" +
        "3\t\t571.296\t\t40.0028\n" +
        "3\t\t599.766\t\t39.8304\n" +
        "3\t\t621.736\t\t39.8304\n" +
        "3\t\t665.499\t\t39.8304\n" +
        "3\t\t687.859\t\t39.8304\n" +
        "4\t\t115.997\t\t503.877\n" +
        "4\t\t115.62\t\t324.0\n" +
        "4\t\t115.62\t\t40.5\n" +
        "4\t\t121.715\t\t215.873\n" +
        "4\t\t188.51\t\t432.749\n" +
        "4\t\t200.49\t\t432.75\n" +
        "4\t\t220.99\t\t432.75\n" +
        "4\t\t233.98\t\t432.75\n" +
        "4\t\t244.96\t\t432.75\n" +
        "4\t\t255.95\t\t432.75\n" +
        "4\t\t266.94\t\t432.75\n" +
        "4\t\t278.92\t\t432.75\n" +
        "4\t\t288.55\t\t432.75\n" +
        "4\t\t299.294\t\t432.749\n" +
        "4\t\t312.88\t\t432.75\n" +
        "4\t\t321.887\t\t432.75\n" +
        "4\t\t188.51\t\t288.75\n" +
        "4\t\t200.49\t\t288.75\n" +
        "4\t\t220.99\t\t288.75\n" +
        "4\t\t233.98\t\t288.75\n" +
        "4\t\t244.96\t\t288.75\n" +
        "4\t\t255.95\t\t288.75\n" ;

        List<TestSort> testSortList = new ArrayList<TestSort>();
        String[] fieldList = fields.split("\n");
        for (String field : fieldList) {
            String[] threeVals = field.split("\t\t");
            TestSort df = new TestSort();
            df.setPage(!"null".equals(threeVals[0]) ? Integer.valueOf(threeVals[0]) : null);
            df.setTopLeftY(Double.valueOf(threeVals[1]));
            df.setTopLeftX(Double.valueOf(threeVals[2]));
            df.setFormField("");
            testSortList.add(df);
        }
        Collections.sort(testSortList, new Comparator<TestSort>() {
            @Override
            public int compare(TestSort o1, TestSort o2) {
                if (o1.getPage() == null || o2.getPage() == null || o1.getPage().equals(o2.getPage())) {
                    if (o1.getTopLeftY() == null || o2.getTopLeftY() == null || o1.getTopLeftY().equals(o2.getTopLeftY())) {
                        if (o1.getTopLeftX() == null || o2.getTopLeftX() == null || o1.getTopLeftX().equals(o2.getTopLeftX())) {
                            return o1.getFormField().compareTo(o2.getFormField());
                        } else {
                            return o1.getTopLeftX().compareTo(o2.getTopLeftX());
                        }
                    } else {
                        return o1.getTopLeftY().compareTo(o2.getTopLeftY());
                    }
                } else {
                    return o1.getPage().compareTo(o2.getPage());
                }
            }
        });
    }

    public class TestSort {
        private Integer page;
        private Double topLeftY;
        private Double topLeftX;
        private String formField;

        public String getFormField() {
            return formField;
        }

        public void setFormField(String formField) {
            this.formField = formField;
        }

        public Integer getPage() {
            return page;
        }

        public void setPage(Integer page) {
            this.page = page;
        }

        public Double getTopLeftY() {
            return topLeftY;
        }

        public void setTopLeftY(Double topLeftY) {
            this.topLeftY = topLeftY;
        }

        public Double getTopLeftX() {
            return topLeftX;
        }

        public void setTopLeftX(Double topLeftX) {
            this.topLeftX = topLeftX;
        }
    }
}

异常表明这是一个非传递比较。我不得不同意 Dave Newton 的评论,即很难对这个比较器进行推理。所有的空检查都令人困惑,但似乎比较事物的顺序并不一致。请参阅 Effective Java,第 12 项(考虑实施 Comparable):

If a class has multiple significant fields, the order in which you compare them is critical. You must start with the most significant field and work your way down. If a comparison results in anything other than zero (which represents equality), you’re done; just return the result. If the most significant fields are equal, go on to compare the next-most-significant fields, and so on. If all fields are equal, the objects are equal; return zero.

具体来说,如果您将空字段与非空字段进行比较,您的代码将尝试继续并比较下一个最重要的字段,这意味着您正在处理一个字段为空且另一个不是,所以一个应该比另一个排序更高,并且您将它们视为等效,但您要继续比较下一个最重要的字段。

这是一个遵照 Bloch 的建议编写的比较器,它试图清除空检查;空值被替换为低排序值,因此顺序将保持一致:

class TestSortComparator implements Comparator<TestSort> {

    Double defaultIfNull(Double d) {
        return d == null ? Double.NEGATIVE_INFINITY : d;
    }

    Integer defaultIfNull(Integer i) {
        return i == null ? Integer.MIN_VALUE : i;
    }

    String defaultIfNull(String s) {
        return s == null ? "" : s;
    }

    @Override
    public int compare(TestSort o1, TestSort o2) {
        int pageComp = defaultIfNull(o1.getPage())
            .compareTo(defaultIfNull(o2.getPage()));
        if (pageComp != 0) return pageComp;
        int yComp = defaultIfNull(o1.getTopLeftY())
            .compareTo(defaultIfNull(o2.getTopLeftY()));
        if (yComp != 0) return yComp;
        int xComp = defaultIfNull(o1.getTopLeftX())
            .compareTo(defaultIfNull(o2.getTopLeftX()));
        if (xComp != 0) return xComp;
        return defaultIfNull(o1.getFormField())
            .compareTo(defaultIfNull(o2.getFormField()));
    }
}

correctly summarizes the problem with your Comparator; it is not obeying the contract with regards to the transitivity of comparisons. From the Javadocs of compareTo():

The implementor must also ensure that the relation is transitive:

((compare(x, y) > 0) && (compare(y, z) > 0))
implies

compare(x, z) > 0

编写比较一个对象的多个属性的空安全比较器可能很困难——编写一个实际上正确的实现更难。检查是否为空并跟踪先前比较的值会使代码变得笨拙和冗长。

出于上述原因,最好使用适当的框架来使代码更易读且更不容易出错。在 Google Guava's ComparisonChain class 的帮助下,实现流利的 Comparator 很容易(也很有趣)。这是 Comparator 使用 ComparisonChain:

的样子
static class FluentComparator implements Comparator<TestSort> {

    @Override
    public int compare(TestSort o1, TestSort o2) {
        return ComparisonChain
                .start()
                .compare(o1.getPage(), o2.getPage(),
                        Ordering.natural().nullsFirst())
                .compare(o1.getTopLeftY(), o2.getTopLeftY())
                .compare(o1.getTopLeftX(), o2.getTopLeftX())
                .compare(o1.getFormField(), o2.getFormField())
                .result();
        }
   }

请注意您可以多么流畅地将不同的比较链接成 "one-liner"。此外,链中的每个比较都是 延迟评估(即仅在需要时),从而无需编写多个 return 语句即可高效实现。来自 docs:

ComparisonChain performs a "lazy" comparison: it only performs comparisons until it finds a nonzero result, after which it ignores further input.

但是,您需要小心处理可为 null 的属性。由于 getPage() 可以 return null 在您的示例数据中,我们需要使用 compare 的重载,它接受可为空的输入和处理 nulls 的策略( null在这种情况下是第一个)。