比较两个对象(不包括某些字段) - Java

Compare two objects excluding some fields - Java

提问人:Anamika Patel 提问时间:12/23/2021 更新时间:2/11/2023 访问量:11351

问:

我需要比较同一类的两个对象,不包括某些字段。

public final class Class1 {
  private String a;
  private String b;
  private String c;
:
:
:

  private String z;
  private Date createdAt; 
  private Date updatedAt; 
} 

我怎样才能找到上述类的两个对象是否相等,不包括 createdAt 和 updatedAt 值?由于这门课中有很多字段,我不想逐一比较。

请不要给出 AssertJ 的递归比较解决方案,因为我不需要它用于 UnitTests。

提前谢谢你!

Java 等于 相等

评论

1赞 Turing85 12/23/2021
相应地覆盖 Object::equalsObject::hashCode,或编写相应的 Comparator
0赞 fantaghirocco 12/23/2021
反思
0赞 Turing85 12/23/2021
@fantaghirocco我建议只将反思作为最后的手段。

答:

3赞 Prog_G 12/23/2021 #1

尝试覆盖等于方法,如下所示:

import java.util.Date;
import java.util.Objects;

public final class Class1 {
    private String a;
    private String b;
    private String c;
    private String z;
    private Date createdAt;
    private Date updatedAt;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Class1 class1 = (Class1) o;
        return Objects.equals(a, class1.a) && Objects.equals(b, class1.b) && Objects.equals(c, class1.c) && Objects.equals(z, class1.z);
    }

    @Override
    public int hashCode() {
        return Objects.hash(a, b, c, z);
    }
}

评论

0赞 tannerli 12/23/2021
这似乎正是 OP 想要避免的策略,因为据说类中有许多省略的字段
2赞 Prog_G 12/23/2021
如果不对有效字段进行一对一比较,就不能说对象相等或不相等。
7赞 Turing85 12/23/2021 #2

如果覆盖 Object::equals 和 Object:hashCode 不是一个选项,我们可以使用 Comparator API 来构造相应的比较器:

final Comparator<Class1> comp = Comparator.comparing(Class1::getA)
        .thenComparing(Class1::getB)
        .thenComparing(Class1::getC)
        .
        .
        .
        .thenComparing(Class1::getZ);

不幸的是,如果不比较所有应该相等的字段,就无法做到这一点。

0赞 JayC667 12/23/2021 #3

除了 Comparator 和 / 方法之外,您还可以使用 Reflections。hashCode()equals

  1. 创建注释以排除某些字段:

黑名单例:

@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.FIELD) //on class level
public @interface IngoreForEqualCheck { /* tagging only */ }
  1. 使用 Reflection 来分析要比较的对象,方法是使用 和/或对象的类。这甚至可能是不同的类。pClass.getFields()pClass.getDeclaredFields()

  2. 遍历所有未标记为忽略的字段,比较值。

优化

  1. 要从上面扩展黑名单,还要引入白名单:还要创建一个注释以仅检查这些字段。UseForEqualCheck

  2. 为了提高速度,在分析相应的类及其字段时,您可以创建要检查的字段的可迭代列表,而不是每次都进行反射字段分析,只需使用列表即可。

  3. 通常,您将对检测到的字段值使用。您还可以 a) 使用另一个自定义注释标记类,或 b) 检查字段是否有任何白名单/黑名单注释,以便可靠地将新方法用于嵌入/继承/委托的注释类。equals()

警告

与所有反射一样,在分析类的层次结构时可能会遇到麻烦,这些类在编译过程 (javac) 期间被注释预处理器或字节码编织修改过。这主要是指 Java EE 又名 Jakarta,但可能发生在将幕后功能合并到代码中或更改运行时行为的任何地方,例如注入、面向方面的库等。

8赞 Renis1235 12/23/2021 #4

无需编写任何代码的最快方法是Lombok

Lombok 是 Java 中最常用的库之一,它从您的项目中占用了大量样板代码。如果您需要阅读更多关于它的功能和作用,请转到此处

实现所需内容的方法非常简单:

// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
@EqualsAndHashCode(onlyExplicitlyIncluded = true) 
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public class Class1
{

  @EqualsAndHashCode.Include // Include this field
  private Long identity;
  
  private String testStr1; // This field is not annotated with Include so it will not be included in the functions.

  // ... any other fields
}

龙目岛可以做的远不止于此。有关更多信息,请参阅此处@EqualsAndHashCode

您始终可以更快地解决您的用例:@EqualsAndHashCode.Exclude

@EqualsAndHashCode
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public final class Class1 {
  private String a;
  private String b;
  private String c;
:
:
:

  private String z;

  @EqualsAndHashCode.Exclude
  private Date createdAt; 
  @EqualsAndHashCode.Exclude
  private Date updatedAt; 
} 
0赞 Lior Halfon 8/31/2022 #5

@Renis1235龙目岛的解决方案是最简单的。 但是,如果由于某种原因,在不同的上下文中,equals 可能意味着不同的东西,并且您不想更改默认的 Equals 和 Hashcode 行为,我建议将默认值分配给要排除的字段,然后使用等于。 (假设您可以更改对象)

例如:

Class1 a = ...;
Class1 b = ...;
a.createdAt = null;
b.createdAt = null;
a.updatedAt = null;
b.updatedAt = null;
boolean isEqualExcludingTimestamps = a.equals(b);
1赞 KevinC 2/11/2023 #6

使用 Apache Commons-Lang

CompareToBuilder.reflectionCompare(expected, argument, "someField");