23

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

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

我想为这个类写一个 BarComparator,

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

有没有一种标准的方法来处理任何 , o1, o2,o1.bar中的任何一个o2.bar都可以null,而无需编写大量嵌套的if... else

干杯!

4

10 回答 10

37

我想你可以用一个小的静态方法来包装对字段 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);
}
于 2008-09-24T16:30:06.103 回答
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领域以保持重点。

于 2008-09-25T14:25:09.263 回答
6

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

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

于 2008-09-24T16:12:05.707 回答
3

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

当然,在最后一种情况下,您会抛出异常。对于其他人,您需要一个四向 if/else 案例(大约三分钟的编码一个您已经制定出您想要的结果)。

于 2008-09-24T17:17:45.410 回答
3

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

于 2008-09-24T18:54:21.230 回答
3

你可以为它写你的比较器。假设您有一个以字符串名称作为私有字段的类 Person。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 用于 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));
于 2015-10-02T18:58:09.720 回答
3

您还可以使用 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'}
于 2015-10-28T13:14:17.713 回答
2

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

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

或者

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));
于 2018-01-12T14:08:45.953 回答
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);
    }
}
于 2008-09-25T17:06:22.217 回答
0

我认为早期返回语句将是许多 if 的另一种选择

例如

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.
于 2008-09-24T16:07:09.060 回答