8

我正在编写一些自定义比较器,我希望它们将空项目推到列表的底部,无论我是按升序还是降序排序。解决这个问题的好策略或模式是什么?

副手:

  • 只需编写单独的升序和降序比较器,尽可能共享代码
  • 通过抛出 NPE 或显式调用它,将 null 处理委托给另一个类
  • 包括一个升旗并在其中放置条件逻辑以绕过空值
  • 在空处理类中包装常规比较器

还有其他策略吗?我想听听不同方法的任何经验,以及各种策略的任何陷阱。

4

5 回答 5

12

我同意 Jon Skeet(这很容易:)。我尝试实现一个非常简单的装饰器

class NullComparators {

    static <T> Comparator<T> atEnd(final Comparator<T> comparator) {
        return new Comparator<T>() {

            public int compare(T o1, T o2) {
                if (o1 == null && o2 == null) {
                    return 0;
                }

                if (o1 == null) {
                    return 1;
                }

                if (o2 == null) {
                    return -1;
                }

                return comparator.compare(o1, o2);
            }
        };
    }

    static <T> Comparator<T> atBeginning(final Comparator<T> comparator) {
        return Collections.reverseOrder(atEnd(comparator));
    }
}

给定一个比较器:

Comparator<String> wrapMe = new Comparator<String>() {
      public int compare(String o1, String o2) {
          return o1.compareTo(o2);
      }
};

和一些测试数据:

List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null);

您可以在最后使用空值进行排序:

Collections.sort(strings, NullComparators.atEnd(wrapMe));
[aaa, bbb, ccc, null, null, null]

或在开始时:

Collections.sort(strings, NullComparators.atBeginning(wrapMe));
[空,空,空,ccc,bbb,aaa]
于 2009-08-11T18:28:22.113 回答
6

最后一个选项对我很有吸引力。比较器非常适合链接在一起。特别是你可能想写 aReverseComparatorNullWrappingComparator.


编辑:你不必自己写这个。如果您查看Google Collections Library中的Ordering类,您会发现这个和其他各种好东西 :)


编辑:更详细地说明我的意思ReverseComparator......

一个警告词 - 在 a 的实现中ReverseComparator,颠倒参数的顺序而不是否定结果,否则Integer.MIN_VALUE将“颠倒”到自身。

所以这个实现是错误的(假设original是比较器要反转):

public int compare(T x, T y)
{
    return -original.compare(x, y);
}

但这是正确的:

public int compare(T x, T y)
{
    return original.compare(y, x);
}

原因是我们总是想反转比较,但是如果original.compare(x, y)返回int.MIN_VALUE,那么坏的比较器也会返回int.MIN_VALUE这是不正确的。这是由于int.MIN_VALUE == -int.MIN_VALUE.

于 2009-08-11T17:56:53.957 回答
6

跟进 dfa 的回答 - 我想要的是空值在最后排序而不影响非空值的顺序。所以我想要更多类似的东西:

public class NullComparatorsTest extends TestCase {
    Comparator<String>  forward = new Comparator<String>() {
                                    public int compare(String a, String b) {
                                        return a.compareTo(b);
                                    }
                                };

    public void testIt() throws Exception {
        List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null);
        Collections.sort(strings, NullComparators.atEnd(forward));
        assertEquals("[aaa, bbb, ccc, null, null, null]", strings.toString());
        Collections.sort(strings, NullComparators.atBeginning(forward));
        assertEquals("[null, null, null, aaa, bbb, ccc]", strings.toString());
    }
}

public class NullComparators {
    public static <T> Comparator<T> atEnd(final Comparator<T> comparator) {
        return new Comparator<T>() {
            public int compare(T a, T b) {
                if (a == null && b == null)
                    return 0;
                if (a == null)
                    return 1;
                if (b == null)
                    return -1;
                return comparator.compare(a, b);
            }
        };
    }

    public static <T> Comparator<T> atBeginning(final Comparator<T> comparator) {
        return new Comparator<T>() {
            public int compare(T a, T b) {
                if (a == null && b == null)
                    return 0;
                if (a == null)
                    return -1;
                if (b == null)
                    return 1;
                return comparator.compare(a, b);
            }
        };
    }
}

不过,完全归功于 dfa - 这只是对他的工作的一个小修改。

于 2009-08-11T18:56:00.123 回答
5

在 Java 8 中,您可以使用Comparator.nullsLastComparator.nullsFirst静态方法来获得对 null 更友好的比较器。假设你有一个Fruit类似下面的类:

public class Fruit {
    private final String name;
    private final Integer size;

    // Constructor and Getters
}

如果您想按大小对一堆水果进行排序并将nulls 放在最后:

List<Fruit> fruits = asList(null, new Fruit("Orange", 25), new Fruit("Kiwi", 5));

你可以简单地写:

Collections.sort(fruits, Comparator.nullsLast(Comparator.comparingInt(Fruit::getSize)));

结果将是:

[Fruit{name='Kiwi', size=5}, Fruit{name='Orange', size=25}, null]
于 2016-08-08T20:09:31.027 回答
2

你总是可以NullComparator从 commons-collections 中使用。它的存在时间比 Google Collections 长。

于 2009-08-12T01:31:13.000 回答