无论如何,将空值排序到底部的一般方法很好吗?

时间:2009-08-11 17:47:17

标签: java design-patterns comparator

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

Offhand:

  • 简单地写单独的升序和 降序比较器,共享代码 在可能的情况下
  • 将null处理委托给另一个 通过投掷NPE或者 通过明确地调用它
  • 加入升序旗帜并放置 导航中的条件逻辑 围绕零点
  • 包装常规比较器 null处理类

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

5 个答案:

答案 0 :(得分:11)

我同意Jon Skeet(这很容易:)。我试图实现一个非常简单的decorator

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));
[null, null, null, ccc, bbb, aaa]

答案 1 :(得分:6)

最后一个选项对我很有吸引力。比较器非常适合链接在一起。特别是您可能希望编写ReverseComparator以及NullWrappingComparator


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


编辑:详细说明我对ReverseComparator ...

的意思

警告的一个词 - 在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

这个有趣的属性引起的

答案 2 :(得分:5)

跟进dfa的回答 - 我想要的是nulls在最后排序而不影响非null的顺序。所以我想要更多的内容:

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完全赞同 - 这只是对他工作的一个小修改。

答案 3 :(得分:3)

在Java 8中,您可以使用Comparator.nullsLastComparator.nullsFirst静态方法来获得更多无效的比较器。假设您有一个Fruit类,如下所示:

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

    // Constructor and Getters
}

如果你想按照它们的大小对一堆水果进行排序,并将null放在最后:

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]

答案 4 :(得分:2)

您总是可以使用来自commons-collections的NullComparator。它比Google Collections长得多。