【问题标题】:Nice general way to sort nulls to the bottom, regardless?无论如何,将空值排序到底部的好通用方法?
【发布时间】:2010-11-18 17:11:11
【问题描述】:

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

副手:

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

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

【问题讨论】:

    标签: java design-patterns comparator


    【解决方案1】:

    我同意 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));
    
    [空,空,空,ccc,bbb,aaa]

    【讨论】:

    • 非常好!谢谢你。如果两个参数都为空,是否有理由不返回 0?我知道 null 行为与非 null 行为不太一样,并且说两个 null 相等是有问题的 - 但是说一个超过另一个更值得怀疑吗?
    • @Carl:正是我刚刚对您的帖子提出的观点:) Comparator 应该在传递两个空值时返回 0 或抛出异常,否则它违反了接口合同。
    【解决方案2】:

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


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


    编辑:更详细地说明我对 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 的有趣属性。

    【讨论】:

    • 我同意你的所有回答,除了否定比较的结果;如果我发现 Comparator 正在使用减法实现整数比较,我会非常不高兴,因为这种方法有很多陷阱。
    • 我接受这个答案是因为引用了 Google Collections 的 Ordering 类 - 现有的经过验证的代码是最好的解决方案。还有关于 Integer.MIN_VALUE 的警告。但我非常感谢@dfa 的代码,如下所示,并希望我能接受这两个答案。
    【解决方案3】:

    跟进 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 - 这只是对他的工作的一个小修改。

    【讨论】:

    • 一个问题:比较两个空值时不返回0。
    【解决方案4】:

    在 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]
    

    【讨论】:

      【解决方案5】:

      您始终可以使用 commons-collections 中的 NullComparator。它的存在时间比 Google Collections 长。

      【讨论】:

        猜你喜欢
        • 2013-06-15
        • 2014-11-28
        • 2020-01-14
        • 2020-04-24
        • 1970-01-01
        • 2014-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多