Java:null安全compareTo方法

时间:2012-11-22 17:09:09

标签: java compare comparator

之前有人问过,但我没有找到一个体面的实施解释。

public int compareTo(Object o)
{
    if (this == null || o == null)
    { 
        return 0;
    }
    Tok tmp = (Tok) o;      
    if (this.rang < tmp.rang)
    {
        return -1;
    } else if (this.rang > tmp.rang ) {
        return 1;
    } else {
        return 0;
    }
}

我读了两个我发现的类似问题;他们坚持要实施另一种方法。我不明白为什么这不起作用。该方法获取一个额外的对象,它检查它是否是一个有效的实例,或null,如果null只返回0;什么是实现null-safe compareTo的最简单方法。

对我有用的实施是:

public int compareTo(Object o)
{
    if (o == null)
    { 
        return 0;
    }
    Tok tmp = (Tok) o;      
    if (this.rang < tmp.rang)
    {
        return -1;
    } else if (this.rang > tmp.rang ) {
        return 1;
    } else {
        return 0;
    }
}

这不是最佳实施,应该看看好人们在这里发布的答案。对于我的特殊情况,这是足够好,因为它永远不会为null,但接收到的对象可以为null,如果任何一个为null,则初始实现状态返回0.因此,如果给定对象为null,则返回0。

7 个答案:

答案 0 :(得分:12)

就个人而言,我喜欢Guava's Ordering进行无效安全比较。您可以指定#nullsFirst()#nullsLast()以避免NullPointerException s。

其他重要说明,主要来自评论:

  • this
  • 从不 null
  • 如果您正在实施细粒度的compareTo()
  • ,请考虑使用Guava's ComparisonChain
  • 在实施Comparable时,请务必指定type参数,以便您获得编译时类型安全,并且不必使用instanceof或强制转换:

    class Tok implements Comparable<Tok> {
        // snip
    
        public int compareTo(Tok other) {
            // snip
        }
    }
    

答案 1 :(得分:5)

返回0意味着thiso相等,如果o为空则不然。此外,this永远不会为空。

当然,这取决于应用程序。您可能希望拥有一个等于null的对象。你返回的内容取决于你,但是如果你正在寻找一种通用的零安全方法,那么它并不是很理想。

要完全通用,我会检查o是否为null,如果是,则抛出某种异常。

答案 2 :(得分:4)

我对其他答案不满意:
你不应该在compareTo中检查null 要求它抛出NullPointerException,否则你会弄乱你的树,并且很难找到你的TreeMap无法工作的原因。

一种非常值得推荐的方法:

 public int compareTo(Tok other) { 
    int thisRang = this.rang; 
    int otherRang = other.rang; 
    return (thisRang < otherRang ? -1 : (thisRang == otherRang ? 0 : 1)); 
  } 
  public int compareTo(Object other) { 
    return compareTo((Tok)other); 
  } 

进一步使其成为完美级别Tok应该是最终的! (否则你可能会有问题  当你从Tok继承。 (Sun在课堂日期发出错误)

final class Tok {
    int rang;
}

处理compare和equals并不总是很容易,考虑使用HashMap代替Trees(TreeMap),然后你不必实现compareTo。 您应该实现hashCode,只需返回this.rang。

最后,它是高度推荐的,但并非强制要求实现equals()

public boolean equals(Object obj) {
return obj instanceof Tok
    && this.rang() == ((Tok) obj).rang;
}

答案 3 :(得分:1)

比较两个对象可以像任何其他方法一样安全,这里的问题是普通方法有两个参数,但compareTo接收一个,另一个是对象本身。

this永远不能为空,这意味着你在null对象中执行代码(没有实例)。在这种情况下,NullPointerException将在调用compareTo时被抛出,从而无法执行其代码。

与对象一样多的方法,因为比较可以基于可以为null的类的字段(用于排除基元类型的目标)。因此,长话短说,您的空检查应该涵盖您作为compareTo中的参数和使用的字段接收的对象。此外,如果您有一个包含某些逻辑的外部实例(即实用程序类),则应检查该实例是否也为空。

作为旁注,如果任何涉及的对象为null,则返回的任何内容都必须一致并记录(您可以返回-1或1,在开头或结尾放置空值)。只是避免返回0(对于equals对象,返回true的情况与null的情况相同。

答案 4 :(得分:1)

看似奇怪,但不安全。尝试将您的Tok添加到TreeSet或TreeMap(作为键),您将获得NullPointerException。问题是TreeSet的实现是基于TreeMap的。当您尝试添加(null)底层映射时,将尝试将您的null设置为导致NPE

答案 5 :(得分:1)

作者坚持认为他不想从他的中移除空值 托克[]。
这是一个允许使用NULL值排序的灵魂,并且不违反java契约

为避免这种情况,您在类内部Tok中创建了违反compareTo合约的, 你创建一个显式的NullSafeComparator:

 /**
 * This comparator accepts null objects,
 * sorts ascending, null values are after non null values.
 */
public static final class NullSafeComparator implements Comparator<Tok> {
    public int compare(Tok o1, Tok o2) {
        int r1 = Integer.MAX_VALUE;
        int r2 = Integer.MAX_VALUE;
        if (o1 != null) {
            r1 = o1.rang;
        }
        if (o2 != null) {
            r2 = o2.rang;
        }
        return (r1 < r2 ? -1 : (r1 == r2 ? 0 : 1));
    }
}

简化类Tok(删除静态关键字,用于定义一个单元测试类中的所有内容):

public static class Tok {
    int rang;
    public Tok(int rang) {
        this.rang = rang;
    }
    public String toString() {
        return Integer.toString(rang);
    }
}

最后一个单元测试显示:

public void testSort() {

    Tok[] toks = new Tok[5];
    toks[0] = new Tok(3);
    toks[1] = new Tok(1);
    toks[2] = null;
    toks[3] = null;
    toks[4] = new Tok(2);



    Arrays.sort(toks, new NullSafeComparator());



    for (Tok tok: toks) {
        System.out.println(tok);
    }
    assertEquals(1, toks[0]);
    assertNull(toks[4]);
}

这将产生以下预期结果:

1
2
3
null
null

答案 6 :(得分:0)

根据documentation

Note that null is not an instance of any class, and e.compareTo(null) should
throw a NullPointerException even though e.equals(null) returns false.

因此,如果您实现了一个null-safe方法,它的行为将是意外的(也就是说文档不一致,并且可以与API的其余部分一致)。