如何处理compare()中的空字段?

在Java中,我使用一个类,其中一些字段可以为null 。 例如:

 class Foo { String bar; //.... } 

我想为这个class写一个BarComparator,

  private static class BarComparator implements Comparator { public int compare( final Foo o1, final Foo o2 ) { // Implementation goes here } } 

有没有一种标准的方法来处理o1o2o1.baro2.bar中的任何一个都可以为null ,而不需要编写大量的嵌套ifelse

干杯!

我猜你可以用一个小的静态方法来调用字段compareTo方法,以便对高或低的空值进行排序:

 static > int cp(T a, T b) { return a==null ? (b==null ? 0 : Integer.MIN_VALUE) : (b==null ? Integer.MAX_VALUE : a.compareTo(b)); } 

简单用法(多个字段与通常一样):

 public int compare( final Foo o1, final Foo o2 ) { return cp(o1.field, o2.field); } 

谢谢你的回复! 通用方法和Google Comparators看起来很有趣。

我发现Apache Commons Collections中有一个NullComparator (我们目前正在使用它):

 private static class BarComparator implements Comparator { public int compare( final Foo o1, final Foo o2 ) { // o1.bar & o2.bar nulleness is taken care of by the NullComparator. // Easy to extend to more fields. return NULL_COMPARATOR.compare(o1.bar, o2.bar); } private final static NullComparator NULL_COMPARATOR = new NullComparator(false); } 

注意:我专注于bar字段,以保持它的重点。

这取决于您是否将null条目视为值得比较的有效字符串值。 是null <或>“apple”。 我唯一可以肯定的是null == null。 如果您可以定义null适合排序的位置,那么您可以适当地编写代码。

在这种情况下,我可能会选择抛出NullPointerExcpetion或IllegalArgumentException,并尝试在更高级别处理null,而不是首先将它放在比较中。

你可以为它编写比较器。 假设您有一个具有String名称的Person作为私有字段。 getName()和setName()方法来访问字段名称。 下面是类Person的比较器。

  Collections.sort(list, new Comparator() { @Override public int compare(Person a, Person b) { if (a == null) { if (b == null) { return 0; } return -1; } else if (b == null) { return 1; } return a.getName().compareTo(b.getName()); } }); 

更新:

从Java 8开始,您可以在下面的API中使用List。

 // Push nulls at the end of List Collections.sort(subjects1, Comparator.nullsLast(String::compareTo)); // Push nulls at the beginning of List Collections.sort(subjects1, Comparator.nullsFirst(String::compareTo)); 

这里的关键是要弄清楚你希望如何处理空值。 一些选项是:a)假设空值出现在排序顺序中的所有其他对象之前b)假设空值出现在排序顺序中的所有其他对象之后c)将null视为等效于某个默认值d)将空值视为错误条件。 您选择哪一个完全取决于您正在使用的应用程序。

在最后一种情况下,你当然会抛出exception。 对于其他人,你需要一个四向if / else的情况(大约三分钟的编码,你已经找到了你想要的结果)。

如果您使用的是Google集合,则可能会发现Comparators类非常有用。 如果有辅助方法,可以将空值排序为集合中的最大元素或最小元素。 您可以使用复合比较器来帮助减少代码量。

您可以使用Spring Framework中的类org.springframework.util.comparator.NullSafeComparator

示例(Java 8):

 SortedSet foos = new TreeSet<>( ( o1, o2 ) -> { return new NullSafeComparator<>( String::compareTo, true ).compare( o1.getBar(), o2.getBar() ); } ); foos.add( new Foo(null) ); foos.add( new Foo("zzz") ); foos.add( new Foo("aaa") ); foos.stream().forEach( System.out::println ); 

这将打印:

 Foo{bar='null'} Foo{bar='aaa'} Foo{bar='zzz'} 

在我看来,没有一种方法可以做到这一点,但无论如何代码不是那么久。

你不应该像你那样使用NullComparator – 你正在为每个比较操作创建一个新的类实例,例如,如果你要对一个包含1000个条目的列表进行排序,那将是1000 * log2(1000)个对象,完全是多余的。 这很快就会出现问题。

要么将其子类化,要么委托给它,或者只是实现自己的空检查 – 它实际上并不复杂:

 private static class BarComparator implements Comparator { private NullComparator delegate = new NullComparator(false); public int compare( final Foo o1, final Foo o2 ) { return delegate.compare(o1.bar, o2.bar); } } 

我认为早期的退货声明将是许多ifs的另一种选择

例如

 if(o1==null) return x; if(o2==null) return x; if(o1.getBar()==null) return x; if(o2.getBar()==null) return x; // No null checks needed from this point. 

将客户视为POJO。我的回答是:

 Comparator compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId())); 

要么

 Comparator compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));