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

Java 如何处理compare()中的空字段?,java,comparison,null,Java,Comparison,Null,在Java中,我使用一个类,其中一些字段可以是null。例如: class Foo { String bar; //.... } 我想为这个类编写一个BarComparator private static class BarComparator implements Comparator<Foo> { public int compare( final Foo o1, final Foo o2 ) {

在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
        }
    }
私有静态类BarComparator
执行比较器{
公共整数比较(最终Foo o1,最终Foo o2)
{
//实现就在这里
}
}
有没有一种标准的方法来处理这样一个事实,即
o1
o2
o1.bar
o2.bar
中的任何一个都可以
null
,而无需编写大量嵌套的
if
else


干杯

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

e、 g


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


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

我想您可以使用一个小的静态方法将对field compareTo方法的调用包装起来,以对null进行高或低排序:

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));
}

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


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

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

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

我发现(我们目前正在使用的)中有一个:

私有静态类BarComparator
执行比较器
{
公共整数比较(最终Foo o1,最终Foo o2)
{
//o1.bar和o2.bar的空值由空值比较器处理。
//易于扩展到更多领域。
返回空比较器。比较(o1.bar,o2.bar);
}
专用最终静态空比较器空比较器=
新的空比较器(假);
}

注意:我将重点放在了
字段上,以使其切中要害。

您不应该像使用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);
    }
}
私有静态类BarComparator
执行比较器{
私有NullComparator委托=新的NullComparator(false);
公共整数比较(最终Foo o1,最终Foo o2)
{
返回委托比较(o1.bar,o2.bar);
}
}

您可以为它编写比较器。假设您有一个字符串名为private字段的类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());
        }
    });

在Spring框架中还可以使用类
org.springframework.util.comparator.NullSafeComparator

示例(Java8):


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

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

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

Off-topic我知道,但是为什么选择最小/最大值而不是-/+1?很抱歉延迟响应。这是为了确保我们有三角形不等式。对于a>b>c,如果o1/o2为空,a.compareTo(b)+b.compareTo(c)仍然可以抛出
NullPointerException
。您应该如何处理o1/o2为空?OP提到:o1,o2,o1.bar,o2.bar可以为空。还是比较器契约的一部分:比较null应该抛出一个NPE?@Daniel
comparator.compare
可能抛出一个带有
null
参数的NPE。通常最好尽早捕获任何伪空值或任何其他形式的非法参数(尽管我注意到原始问题确实提到了
o1
o2
可能是
null
)。没错,NullComparator应该是一个私有静态字段。我在示例中这样写是为了关注空值。我喜欢这个答案。谢谢似乎到Javadocs的链接已经失效了。它现在位于。
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);
    }
}
    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());
        }
    });
// 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));
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'}
Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId()));
Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));