You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Miguel Munoz (Jira)" <ji...@apache.org> on 2020/06/12 08:27:00 UTC

[jira] [Updated] (LANG-1493) Performance of Reflection for equals and hash code can be improved over EqualsBuilder

     [ https://issues.apache.org/jira/browse/LANG-1493?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Miguel Munoz updated LANG-1493:
-------------------------------
    Description: 
The EqualsBuilder.reflectionEquals() method is very inefficient. It reflects through all the fields of a class every time it needs to do a comparison. A faster approach would be to do the reflection once when the class loads, and re-use it for each instance. This change, however, needs a different API and couldn't be a drop-in replacement for EqualsBuilder. Instead it would need to be a new class.
 I have written a class that can do this. Performance tests show that it runs up to 20 times faster, depending on how many fields it needs to compare before finding a difference.

You can try the class out by going to [https://github.com/SwingGuy1024/DogTags].

The API currently works like this:
  
{code:java}
public class MyClass {
  // fields and methods omitted for brevity
  
  // Use reflection to implement equals() and hashCode() based on all non transient, non static fields
  private static final DogTag.Factory<MyClass> factory = DogTag.create(MyClass.class).build();
  private final DogTag<MyClass> dogTag = factory.tag(this);
  
  @Override
  public boolean equals(Object that) {
    return dogTag.equals(that);
  }
  
  @Override
  public int hashCode() {
    return dogTag.hashCode();
  }
}{code}
An alternative way to do this is to specify function pointers
{code:java}
public class MyClass2 {
  private String name;
  private int age;
  
  public MyClass2(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() { return name;}
  public int getAge() { return age;}
  
  private static final DogTag.Factory<MyClass2> factory = DogTag.createByLambda(MyClass2.class)
      .addSimple(MyClass2::getAge)
      .addObject(MyClass2::getName)
      .build();
  private final DogTag<MyClass2> dogTag = factory.tag(this);

  @Override
  public int hashCode() {
    return dogTag.hashCode();
  }

  @Override
  public boolean equals(final Object obj) {
    return dogTag.equals(obj);
  }
}

{code}
(I'm a new contributor, so I'm not sure if this is the best way to suggest a whole new class. But I'd like to contribute to give people a faster way of doing Reflection Equals.)

  was:
The EqualsBuilder.reflectionEquals() method is very inefficient. It reflects through all the fields of a class every time it needs to do a comparison. A faster approach would be to do the reflection once when the class loads, and re-use it for each instance. This change, however, needs a different API and couldn't be a drop-in replacement for EqualsBuilder. Instead it would need to be a new class.
 I have written a class that can do this. Performance tests show that it runs up to 20 times faster, depending on how many fields it needs to compare before finding a difference.

You can try the class out by going to [https://github.com/SwingGuy1024/DogTags].

The API currently works like this:
 
{code:java}
public class MyClass {
  // fields and methods
  
  private static final DogTag<MyClass> dogTag = DogTag.from(MyClass.class);
  
  @Override
  public boolean equals(Object that) {
    return dogTag.doEqualsTest(this, that);
  }
  
  @Override
  public int hashCode() {
    return dogTag.doHashCode(this);
  }
}{code}

I'm looking into changing the API, so it would work like this:


{code:java}
public class MyClass {
  // fields and methods here ...
  
  private final DogTag<MyClass> dogTag = DogTag.from(MyClass.class, this);
  
  @Override
  public boolean equals(Object other) {
    return dogTag.doEqualsTest(other);
  }
  
  @Override
  public int hashCode() {
    return dogTag.doHashCode();
  }
}
{code}

(I'm a new contributor, so I'm not sure if this is the best way to suggest a whole new class. But I'd like to contribute to give people a faster way of doing Reflection Equals.)


> Performance of Reflection for equals and hash code can be improved over EqualsBuilder
> -------------------------------------------------------------------------------------
>
>                 Key: LANG-1493
>                 URL: https://issues.apache.org/jira/browse/LANG-1493
>             Project: Commons Lang
>          Issue Type: New Feature
>          Components: lang.*
>            Reporter: Miguel Munoz
>            Priority: Major
>   Original Estimate: 672h
>  Remaining Estimate: 672h
>
> The EqualsBuilder.reflectionEquals() method is very inefficient. It reflects through all the fields of a class every time it needs to do a comparison. A faster approach would be to do the reflection once when the class loads, and re-use it for each instance. This change, however, needs a different API and couldn't be a drop-in replacement for EqualsBuilder. Instead it would need to be a new class.
>  I have written a class that can do this. Performance tests show that it runs up to 20 times faster, depending on how many fields it needs to compare before finding a difference.
> You can try the class out by going to [https://github.com/SwingGuy1024/DogTags].
> The API currently works like this:
>   
> {code:java}
> public class MyClass {
>   // fields and methods omitted for brevity
>   
>   // Use reflection to implement equals() and hashCode() based on all non transient, non static fields
>   private static final DogTag.Factory<MyClass> factory = DogTag.create(MyClass.class).build();
>   private final DogTag<MyClass> dogTag = factory.tag(this);
>   
>   @Override
>   public boolean equals(Object that) {
>     return dogTag.equals(that);
>   }
>   
>   @Override
>   public int hashCode() {
>     return dogTag.hashCode();
>   }
> }{code}
> An alternative way to do this is to specify function pointers
> {code:java}
> public class MyClass2 {
>   private String name;
>   private int age;
>   
>   public MyClass2(String name, int age) {
>     this.name = name;
>     this.age = age;
>   }
>   public String getName() { return name;}
>   public int getAge() { return age;}
>   
>   private static final DogTag.Factory<MyClass2> factory = DogTag.createByLambda(MyClass2.class)
>       .addSimple(MyClass2::getAge)
>       .addObject(MyClass2::getName)
>       .build();
>   private final DogTag<MyClass2> dogTag = factory.tag(this);
>   @Override
>   public int hashCode() {
>     return dogTag.hashCode();
>   }
>   @Override
>   public boolean equals(final Object obj) {
>     return dogTag.equals(obj);
>   }
> }
> {code}
> (I'm a new contributor, so I'm not sure if this is the best way to suggest a whole new class. But I'd like to contribute to give people a faster way of doing Reflection Equals.)



--
This message was sent by Atlassian Jira
(v8.3.4#803005)