2008-09-24 203 views
20

在Java中,我使用了一个类,其中一些字段可以是null。例如:如何处理compare()中的空字段?

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

我想写这个类BarComparator,

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

是否有应对的事实,任何o1o2o1.baro2.bar可以null一种标准的方式,没有写很多嵌套if ... else

干杯!

回答

36

我想你可以换电话到外地compareTo方法用小静态方法来进行排序空值过高或过低:

static <T extends Comparable<T>> 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); 
} 
+1

偏离主题我知道,但偏好MIN/MAX_VALUE超过 -/+ 1的原因是什么? – 2008-09-24 17:03:21

+6

抱歉,延迟响应。这是为了确保我们有三角形的不平等。对于a> b> c,a.compareTo(b)+ b.compareTo(c)<= a.compareTo(c)。不是说任何人都会在意... – 2008-10-11 01:00:28

+0

如果o1/o2为空,仍然可以抛出`NullPointerException`。你应该如何处理o1/o2为空? OP提到:o1,o2,o1.bar,o2.bar可以为null。或者是比较合同的一部分:比较空值应该抛出一个NPE? – Daniel 2014-06-18 06:43:08

1

在我看来,没有办法做到这一点,但无论如何代码没有这么长。

0

我认为早期回报声明将成为很多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. 
6

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

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

+0

我喜欢这个答案。谢谢! – Burkhard 2013-03-27 12:58:34

2

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

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

2

如果您使用的是Google收藏,您可能会发现Comparators课程有帮助。如果使用辅助方法将空值排序为集合中最大或最小的元素。您可以使用compound comparators来帮助减少代码量。

8

感谢您的回复!通用方法和Google比较器看起来很有趣。

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

private static class BarComparator 
     implements Comparator<Foo> 
{ 
    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场在这里把它保持到了点。

1

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

要么子类,或委托给它,或者干脆实现自己的空检查 - 它真的没有那么复杂:

private static class BarComparator 
     implements Comparator<Foo> { 
    private NullComparator delegate = new NullComparator(false); 

    public int compare(final Foo o1, final Foo o2) 
    { 
     return delegate.compare(o1.bar, o2.bar); 
    } 
} 
3

你可以写你比较吧。假设你有一个String类名为Private field的类。 getName()和setName()方法来访问字段名称。以下是Person类的比较器。

Collections.sort(list, new Comparator<Person>() { 
     @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用于名单。

// 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)); 
2

在Spring Framework中还有类org.springframework.util.comparator.NullSafeComparator可以使用。

例(Java 8):

SortedSet<Foo> 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'} 
0

考虑客户的POJO.My的答案是:

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

或者

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