检查Java中两个对象是否完全相同

时间:2015-10-13 13:39:01

标签: java

我有一个Java类,这是一个例子:

public class Car {

    private int fuelType;
    private Date made;
    private String name;
.
.
. // and so on

现在假设我有两个车对象,我想比较它们的所有变量是否相等。

现在,我通过重写方法equals(Object o)解决了这个问题,并检查两个对象中的所有变量是否匹配。

这里的问题是,如果我有20个课程,我将不得不在每个课程中覆盖equals(Object o)

有没有办法创建某种通用方法,可以比较我传递给它的两个对象中的任何一个,并让我知道它们是否匹配每个变量?

7 个答案:

答案 0 :(得分:22)

您可以选择使用Equals& amp; Hashcode(选项#3 BLEW MY MIND!):

  1. 您的IDE。我不推荐它用于大多数对象,因为它们可以慢慢地与实际的类定义过时。它们看起来也很丑陋并且用样板代码污染了你的代码库。
  2. Apache Commons有一堆东西可以让这更容易,包括一个reflective version,所以没有风险与类定义过时。它比#1更好,除非你需要一个快速的等号/哈希码,但仍然是我喜欢的太多样板。
  3. Project Lombok和注释处理。在ya类上打一个EqualsAndHashCode注释并完成它。 我建议使用Project Lombok 。它为构建添加了一些魔力(但并不多),因此需要一个IDE的插件才能很好地运行,但它们是一个很小的代价,无需样板代码。 Lombok是一个在编译时运行的注释处理器,因此您不会遇到运行时性能损失。
  4. 使用支持它的其他语言,但也以JVM为目标。 Groovy使用annotationKotlin支持data classes。除非您现有的代码可以快速转换,否则我会避免这种情况。
  5. Google's Auto有一个AutoValue。就像Project Lombok一样,这是一个注释处理器,但是以更少的样板而牺牲了更少的魔力(感谢Louis Wasserman

答案 1 :(得分:4)

你可以使用:

 org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare(Object lhs, Object rhs);

它使用反射来比较文件 这是javadoc:javadoc

答案 2 :(得分:4)

我会对大多数人持反对意见(在反思中使用apache commons):是的,这是你必须写的一些代码(让你的IDE真正生成),但你只需要做一次,需要实现equals / hashcode的数据类的数量通常是可管理的 - 至少在我工作的所有大型项目(250k + LOC)中都是如此。

当然,如果您向该类添加一个新成员,您将不得不记住更新equals / hashcode函数,但这通常很容易注意到,最迟在代码审查期间。

老实说,如果你使用一个简单的小助手类,甚至在Java7中,你可以减少Wana Ant显示的代码。真正需要的是:

@Override
public boolean equals(Object o) {
    if (o instanceof Car) { // nb: broken if car is not final - other topic
        Car other = (Car) o;
        return Objects.equals(fuelType, other.fuelType) && 
               Objects.equals(made, other.made) && 
               Objects.equals(name, other.name);
    }
    return false;
}

类似于hashcode:

@Override
public int hashCode() {
    return Objects.hash(fuelType, made, name);
}

不像反射解决方案那么短吗?没错,但它很简单,易于维护,适应和读取 - 性能要好一些(对于实现equals和hashcode的类通常很重要)

答案 3 :(得分:3)

通常,您可以通过IDE生成equals / hashCode方法 - 此领域的所有大型参与者都能够(Eclipse,IntelliJ Idea和Netbeans)。

通常,您可以创建一些使用反射的代码,但我不建议使用反射,因为客观方法更清晰,更易于维护。反射也不会像“标准”那样快。如果你真的想这样,那就有EqualsBuilderHashCodeBuilder等实用程序。

仅供参考,有基于JVM的语言已经支持这些功能,例如: Kotlin data classes,可以很好地用于现有的Java项目中。

答案 4 :(得分:2)

我会为我最喜欢的解决方案插入一个插件:@AutoValue

这是Google提供的一个开源项目,它提供了一个注释处理器,可以为您生成实现equalshashCode的合成类。

由于它是自动生成的代码,因此您无需担心会意外忘记字段或弄乱equalshashCode实施。但由于代码是在编译时生成的,因此运行时开销为零(与基于反射的解决方案不同)。它也是“API不可见” - 你班级的用户无法区分@AutoValue类型和你自己实现的类型,并且你可以在不打破来电者的情况下来回改变。

另见this presentation,它解释了基本原理,并将其与其他方法相比做得更好。

答案 5 :(得分:1)

从理论上讲,你可以使用反射来创建某种类型的util,正如许多人在评论中建议的那样。就个人而言,我不建议你这样做。你会得到一些部分有效的东西。

Java中的许多内容都依赖于equalhashCode,例如方法contains,您可以在实现Collection的任何内容中找到它。

建议覆盖equal(和hashCode)。此外,我认为任何体面的IDE都可以选择为您生成它们。因此,您可以比使用反射更快地完成它。

答案 6 :(得分:0)

这就是我这样做的方式:

@Override
public boolean equals(Object obj) {
    if (obj instanceof Car) {
        return internalEquals((Car) obj);
    }
    return super.equals(obj);
}

protected boolean internalEquals(Car other) {
    if(this==other){
        return true;
    }
    if (other != null) {
        //suppose fuelType can be Integer.
        if (this.getFuelType() !=null) {
            if (other.getFuelType() == null) {
                return false;
            } else if (!this.getFuelType().equals(other.getFuelType())) {
                return false;
            }
        } else if(other.getFuelType()!=null){
            return false;
        }
        if (this.getName() != null) {
            if (other.getName() == null) {
                return false;
            } else if (!this.getName().equals(other.getName())) {
                return false;
            }
        }
        else if(other.getName()!=null){
            return false;
        }
         if (this.getDate() != null) {
            if (other.getDate() == null) {
                return false;
            } else if (!this.getDate().getTime()!=(other.getDate().getTime())) {
                return false;
            }
        }
        else if(other.getDate()!=null){
            return false;
        }
        return true;
    } else {
        return false;
    }
}

编辑
简化版

     public class Utils{
          /**
           * Compares the two given objects and returns true, 
           * if they are equal and false, if they are not.
           * @param a one of the two objects to compare
           * @param b the other one of the two objects to compare
           * @return if the two given lists are equal.
           */
           public static boolean areObjectsEqual(Object a, Object b) {

               if (a == b){
                  return true;
               }
               return (a!=null && a.equals(b));
          }

          public static boolean areDatesEqual(Date a, Date b){
             if(a == b){
                return true;
             }
             if(a==null || b==null){ 
                return false;
             }
             return a.getTime() == b.getTime();
          }
   }

   @Override
   public boolean equals(other obj) {
      if(this == other){
         return true;
      } 
      if(other == null){
          return false;
      }
      if (other instanceof Car) {
          return internalEquals((Car) other);
      }
      return super.equals(obj);
   }

   protected boolean internalEquals(Car other) {        
        //suppose fuelType can be Integer.
        if (!Utils.areObjectsEqual(this.getName(), other.getName()){                   
            return false;
        }
        if (!Utils.areObjectsEqual(this.getName(), other.getName()){ 
            return false;
        }
        if (!Utils.areDatesEqual(this.getDate(), other.getDate()){ 
            return false;
        } 
        return true;
    }
}

也不要忘记哈希码,他们手拉手编码。