2009-08-11 107 views
7

我正在写一些自定义比较器,并且我希望他们将空项目推送到列表底部,而不管我是按升序还是降序排序。接近这个的好策略或模式是什么?不管一般的方法将空值排序到底部?

随口说说:

  • 只需编写单独的上升和 下降比较,共享代码 在可能的情况
  • 代表空处理另一个 类,无论是通过调用它明确
  • 抛出NPE或
  • 包含一个升序标志,并在其中放置 条件逻辑以在空值周围导航
  • 将常规比较器包装在 空处理类

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

回答

5

最后一个选项对我很有吸引力。比较器真的很好链接在一起。特别是你可能想写一个ReverseComparator以及一个NullWrappingComparator


编辑:你不必自己写这个。如果你看一下在Google Collections LibraryOrdering类,你会发现这和其他各种好吃的东西:)


编辑:走进更详细地说明我的意思大约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

+0

我同意你的所有答案,除非否定比较结果;如果我发现比较器使用减法实现整数比较,那么我会相当不高兴,因为该方法有很多缺陷。 – jprete 2009-08-11 19:08:13

+1

@jprete:我想你误解了我。我会编辑。 – 2009-08-11 19:13:39

+1

由于参考Google集合的订购类,我接受此答案 - 现有经过验证的代码是最佳解决方案。还针对有关Integer.MIN_VALUE的警告。但是我非常感谢@dfa的代码,并希望我能接受这两个答案。 – 2009-08-11 23:05:46

10

我同意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] 
+3

非常好!谢谢。如果两个参数都为空,是否有返回0的理由?我知道空行为不像非空行为,并且说两个空值是相等的是值得怀疑的 - 但是说一个超过另一个是否可疑? – 2009-08-11 18:36:56

+2

@Carl:准确地说,我刚刚对你的帖子提出了一点要求:) Comparator *应该返回0,或者在传递两个空值时抛出异常,否则它将违反接口合约。 – 2009-08-11 19:20:07

5

DFA的回答跟进 - 我想要的是,零点排序在不影响非空的顺序结束。所以,我想要的东西沿着这个线路更多:到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); 
      } 
     }; 
    } 
} 

完全学分制,虽然 - 这是他的工作只是一个小的修改。

+1

一个问题:比较两个空值时不返回0。 – 2009-08-11 19:18:45

+0

谢谢;进行更正编辑。 – 2009-08-11 21:02:35

2

您可以随时使用commons-collections的NullComparator。它的历史比Google Collections更长。

3

在Java 8中,可以使用Comparator.nullsLastComparator.nullsFirst静态方法来使用更多的无空值比较器。假设你有一个Fruit类像下面这样:

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

    // Constructor and Getters 
} 

如果要排序一堆水果通过它们的大小,并把null s的结尾:

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] 
相关问题