简体   繁体   English

比较排除某些字段的两个对象 - Java

[英]Compare two objects excluding some fields - Java

I need to compare two objects of the same class excluding some fields.我需要比较相同 class 的两个对象,不包括某些字段。

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

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

How can i find if the two objects of the above class are equal excluding createdAt and updatedAt values?我如何才能找到上述 class 的两个对象是否相等,不包括 createdAt 和 updatedAt 值? Since there are a lot of fields in this class, i don't want to compare each of them one by one.由于这个class中有很多字段,我不想一一比较。

Please don't give AssertJ's recursive comparison solution as I don't need it for UnitTests.请不要提供 AssertJ 的递归比较解决方案,因为 UnitTests 不需要它。

Thank you in Advance!先感谢您!

If overriding Object::equals and Object::hashCode is not an option, we can use the Comparator API to construct a corresponding comparator:如果覆盖Object::equalsObject::hashCode不是一个选项,我们可以使用Comparator器 API来构造一个相应的比较器

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

Unfortunately, there is no way to do this without comparing all fields that should be equal.不幸的是,如果不比较所有应该相等的字段,就没有办法做到这一点。

Try overriding equals method like below:尝试重写 equals 方法,如下所示:

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

I addition to the Comparator and hashCode() / equals method, you could also use Reflections.除了 Comparator 和hashCode() / equals方法之外,您还可以使用反射。

  1. Create an annotation to exclude certain fields:创建注释以排除某些字段:

Blacklisting Example:黑名单示例:

@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.FIELD) //on class level
public @interface IngoreForEqualCheck { /* tagging only */ }
  1. Use Reflection to analyze the objects you want to compare, by using pClass.getFields() and/or pClass.getDeclaredFields() on the objects' class.通过在对象的 class 上使用pClass.getFields()和/或pClass.getDeclaredFields() ,使用反射来分析要比较的对象。 This may be even different classes.这甚至可能是不同的类。

  2. Iterate over all fields that are NOT tagged to be ignored, compare values.遍历所有未标记为被忽略的字段,比较值。

Optimizations优化

  1. To extend on the blacklisting from above, also introduce whitelisting : also create an annotation UseForEqualCheck to only check those fields.要从上面扩展黑名单,还需要引入白名单:还创建一个注释UseForEqualCheck以仅检查这些字段。

  2. For improved speed, when analyzing the respective class and its fields, you can create iterable lists of the fields to check, and instead of doing the Reflection fields analysis each time, simply use the lists.为了提高速度,在分析各个 class 及其字段时,您可以创建要检查的字段的可迭代列表,而不是每次都进行反射字段分析,只需使用列表即可。

  3. Normally you would use equals() on the detected field values.通常您会在检测到的字段值上使用equals() You could also a) tag the class with another custom annotation, or b) check the fields for any whitelisting/blacklisting annotations, so that you will reliably use your new method for embedded/inherited/delegated annotated classes.您还可以 a) 使用另一个自定义注释标记 class,或 b) 检查字段中的任何白名单/黑名单注释,以便您可以可靠地将新方法用于嵌入/继承/委托的注释类。

Warning警告

As with all reflections, you might get into trouble when analyzing hierarchies of classes, that have been modified during the compile process (javac) by annotation preprocessors, or by bytecode weaving.与所有反射一样,在分析类的层次结构时可能会遇到麻烦,这些类在编译过程 (javac) 期间已通过注释预处理器或字节码编织进行了修改。 This mostly refers to Java EE aka Jakarta, but can happen anywhere where behind-the-scenes functionality is incorporated in your code, or runtime behavior is changed, like with injections, aspect oriented libraries etc.这主要是指 Java EE aka Jakarta,但可能发生在代码中包含幕后功能或更改运行时行为的任何地方,例如注入、面向方面的库等。

The quickest way without writing any code is Lombok不写任何代码最快的方法是Lombok

Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. Lombok 是 java 中最常用的库之一,它可以从您的项目中删除大量样板代码。 If you need to read more on what it can and does, go here .如果您需要详细了解它的功能和功能,请点击此处go。

The way to implement what you need is pretty straightforward:实现所需内容的方法非常简单:

// 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
}

Lombok can do a lot more than this.龙目岛可以做的远不止这些。 For more information on @EqualsAndHashCode refer to this .有关@EqualsAndHashCode的更多信息,请参阅

You can always use @EqualsAndHashCode.Exclude for a quicker solution to your use case:您始终可以使用@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; 
} 

@Renis1235 's Lombok solution is the easiest. @Renis1235 的 Lombok 解决方案是最简单的。 However if for some reason in different contexts the equals can mean different things and you don't want to change the default Equals and Hashcode behaviour, I would advice to assign default values to the fields you want to exclude, and then use equals.但是,如果由于某种原因在不同的上下文中,equals 可能意味着不同的东西,并且您不想更改默认的 Equals 和 Hashcode 行为,我建议您为要排除的字段分配默认值,然后使用 equals。 (Assuming you can change the objects) (假设您可以更改对象)

For example:例如:

Class1 a = ...;
Class1 b = ...;
a.createdAt = null;
b.createdAt = null;
a.updatedAt = null;
b.updatedAt = null;
boolean isEqualExcludingTimestamps = a.equals(b);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM