programing

Apache Commons equals/hashCode 빌더

goodsources 2022. 7. 18. 22:19
반응형

Apache Commons equals/hashCode 빌더

분들은 생각하시는지 org.apache.commons.lang.builder EqualsBuilder/HashCodeBuilder를 위해서equals/hashCode직접 쓰는 것보다 더 좋은 방법이 있을까요?이버버 ?? ?? ?? ?? ? ?? 이?신신생 ?각 ?? ????

Commons/Lang 빌더는 훌륭합니다.또, 퍼포먼스 오버헤드는 눈에 띄지 않고(동면 상태 유무에 관계없이) 수년간 사용하고 있습니다.그러나 Alain이 쓴 것처럼 Guava의 방법은 더 좋다.

다음은 Bean 샘플입니다.

public class Bean{

    private String name;
    private int length;
    private List<Bean> children;

}

Commons/Lang에서 구현된 equals() 및 hashCode()는 다음과 같습니다.

@Override
public int hashCode(){
    return new HashCodeBuilder()
        .append(name)
        .append(length)
        .append(children)
        .toHashCode();
}

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return new EqualsBuilder()
            .append(name, other.name)
            .append(length, other.length)
            .append(children, other.children)
            .isEquals();
    } else{
        return false;
    }
}

Java 7 이후 버전(Guava에서 영감을 얻음):

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

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return Objects.equals(name, other.name)
            && length == other.length // special handling for primitives
            && Objects.equals(children, other.children);
    } else{
        return false;
    }
}

주의: 이 코드는 원래 Guava를 참조하고 있었습니다만, 코멘트가 지적한 바와 같이, 이 기능은 이후 JDK에 도입되고 있기 때문에, Guava는 불필요하게 되었습니다.

구아바/JDK 버전은 더 짧고 불필요한 도우미 개체를 방지합니다.는, 이 합선인 는, 의 단락도 .Object.equals() false)가 false(false: commons/lang이 false를 함)ObjectUtils.equals(obj1, obj2)「을로, 「동일한 의미론」 대신에 할 수 .EqualsBuilder을 이용하다

따라서: 네, 커먼 랭 구축자는 수동 구축보다 매우 선호됩니다.equals() ★★★★★★★★★★★★★★★★★」hashCode()(혹은 이클립스가 생성하는 무시무시한 몬스터도 있지만) Java 7+ / Guava 버전은 더 좋습니다.

휴지 상태에 대한 주의사항:

equals(), hashCode() 및 toString() 구현에서 느린 컬렉션을 사용할 때는 주의하십시오.만약 당신이 열린 회기를 갖지 않는다면 그것은 비참하게 실패할 것이다.


주의 (equals()에 대하여):

a) 위의 equals() 버전에서는 다음 숏컷 중 하나 또는 양쪽을 사용할 수도 있습니다.

@Override
public boolean equals(final Object obj){
    if(obj == this) return true;  // test for reference equality
    if(obj == null) return false; // test for null
    // continue as above

b) equals() 계약의 해석에 따라 행을 변경할 수도 있습니다.

    if(obj instanceof Bean){

로.

    // make sure you run a null check before this
    if(obj.getClass() == getClass()){ 

버전을 , 도 하고 것입니다.super(equals())면 insideequals(), 있습니다.토픽은 이 질문에서 설명합니다.

슈퍼클래스를 과바 오브젝트에 통합하는 올바른 방법입니다.hashcode() 실장?

)hashCode() 경우에도 equals())


참고(kayahr의 코멘트에서 영감을 얻음)

Objects.hashCode(..)에 것과 Arrays.hashCode(...)원시 필드가 많으면 성능이 저하될 수 있습니다. 「 」EqualsBuilder더 나은 해결책이 될 수 있습니다.

여러분, 일어나세요!Java 7은 표준 라이브러리 내에 equals와 hashCode도우미 메서드가 있습니다.이러한 사용법은 구아바 방식의 사용법과 완전히 동일합니다.

서드파티 라이브러리(리소스가 제한된 디바이스를 실행하고 있을 가능성이 있음)에 의존하지 않고 독자적인 메서드를 입력하지 않는 경우, 예를 들어 이클립스 사용 등 IDE가 작업을 수행하도록 할 수도 있습니다.

Source -> Generate hashCode() and equals()...

원하는 대로 구성할 수 있고 변경 시 지원해야 하는 '원어민' 코드가 제공됩니다.


예(Eclipse Juno):

import java.util.Arrays;
import java.util.List;

public class FooBar {

    public String string;
    public List<String> stringList;
    public String[] stringArray;

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((string == null) ? 0 : string.hashCode());
        result = prime * result + Arrays.hashCode(stringArray);
        result = prime * result
                + ((stringList == null) ? 0 : stringList.hashCode());
        return result;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        FooBar other = (FooBar) obj;
        if (string == null) {
            if (other.string != null)
                return false;
        } else if (!string.equals(other.string))
            return false;
        if (!Arrays.equals(stringArray, other.stringArray))
            return false;
        if (stringList == null) {
            if (other.stringList != null)
                return false;
        } else if (!stringList.equals(other.stringList))
            return false;
        return true;
    }

}

EqualsBuilder 및 HashCodeBuilder에는 수동으로 작성된 코드와는 크게 다른2가지 측면이 있습니다.

  • 무효 처리
  • 인스턴스 생성

EqualsBuilder와 HashCodeBuilder를 사용하면 늘일 수 있는 필드를 쉽게 비교할 수 있습니다.수동으로 작성한 코드를 사용하면 많은 보일러 플레이트가 생성됩니다.

한편 Equals Builder는 Equals 메서드콜마다 인스턴스를 만듭니다.동등한 메서드가 콜인 경우 많은 인스턴스가 생성됩니다.

휴지 상태일 경우 등가 코드와 해시 코드 구현은 아무런 차이가 없습니다.그것들은 단지 구현의 세부 사항일 뿐입니다.최대 절전 모드로 로드된 거의 모든 도메인 개체에 대해 Builder의 런타임 오버헤드를 무시할 수 있습니다(이스케이프 분석 없이도).데이터베이스와 통신의 오버헤드가 커집니다.

skaffman이 언급했듯이 reflection version은 프로덕션 코드에 사용할 수 없습니다.반영이 느려지고 가장 단순한 클래스를 제외한 모든 클래스에서 "실장"이 올바른 것은 아닙니다.새로 도입된 구성원들이 동등한 방법 행동을 변경하기 때문에 모든 구성원을 고려하는 것도 위험하다.반사 버전은 테스트 코드에서 유용할 수 있습니다.

직접 쓰지 않으면 구글 guava(구 구글 컬렉션)사용할 수도 있습니다.

id가 프라이머리 키인 엔티티 bean만을 취급하는 경우는, 심플화할 수 있습니다.

   @Override
   public boolean equals(Object other)
   {
      if (this == other) { return true; }
      if ((other == null) || (other.getClass() != this.getClass())) { return false; }

      EntityBean castOther = (EntityBean) other;
      return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
   }

특히 어떤 엔티티의 길이, 이름, 아이들을 비교한 답변의 예시는 Hibernate와 잘 어울리지 않는다고 생각합니다.하이버네이트는 equals()와 hashCode()에서 사용하는 비즈니스 키를 사용하도록 권장합니다.이러한 이유는 다음과 같습니다.비즈니스 키로 auto equals() 및 hash Code() 생성기를 사용하는 경우 괜찮습니다.앞에서 설명한 성능 문제만 고려하면 됩니다.하지만 사람들은 IMO가 매우 잘못된 모든 속성을 사용합니다.예를 들어 저는 현재 @AutoProperty에서 Pojomatic을 사용하여 엔티티를 작성하는 프로젝트를 진행하고 있습니다.이것은 매우 나쁜 패턴이라고 생각됩니다.

hashCode()와 equals()를 사용하는 두 가지 주요 시나리오는 다음과 같습니다.

  • 영구 클래스의 인스턴스를 세트에 넣는 경우(다치 어소시에이션을 나타내는 권장 방법) 및
  • 분리된 인스턴스의 재접속을 사용하는 경우

예를 들어 다음과 같은 엔티티가 있다고 가정해 보겠습니다.

class Entity {
  protected Long id;
  protected String someProp;
  public Entity(Long id, String someProp);
}

Entity entity1 = new Entity(1, "a");
Entity entity2 = new Entity(1, "b");

둘 다 하이버네이트에 대해 동일한 엔티티이며, 어느 시점에서 세션에서 가져온 엔티티입니다(ID와 클래스/테이블은 동일).그러나 모든 소품에서 hashCode()를 auto equals()로 구현하면 무엇을 얻을 수 있을까요?

  1. entity1이 이미 존재하는 영속적인 세트에 entity2를 배치하면 두 번 배치되어 커밋 중에 예외가 발생합니다.
  2. entity1이 이미 존재하는 세션에 분리된 entity2를 부가하는 경우 해당 entity2는 올바르게 Marge되지 않습니다(특히 테스트하지 않은 것 같습니다).

따라서 99% 프로젝트에서는 Hibernate 개념과 일치하는 기본 엔티티 클래스에 한 번 기술된 equals()와 hashCode()의 다음 구현을 사용합니다.

@Override
public boolean equals(Object obj) {
    if (StringUtils.isEmpty(id))
        return super.equals(obj);

    return getClass().isInstance(obj) && id.equals(((IDomain) obj).getId());
}

@Override
public int hashCode() {
    return StringUtils.isEmpty(id)
        ? super.hashCode()
        : String.format("%s/%s", getClass().getSimpleName(), getId()).hashCode();
}

일시적인 엔티티에 대해서는, 휴지 상태가 지속성 스텝으로 실시하는 것과 같은 동작을 실시합니다.인스턴스 매치를 사용합니다.영구 객체의 경우 테이블/ID인 고유 키를 비교합니다(복합 키는 사용하지 않습니다).

만약 다른 사용자가 유용하다고 생각할 경우를 대비해서 위에서 언급한 추가 오브젝트 작성 오버헤드를 회피하는 해시 코드 계산용 Helper 클래스를 생각해냈습니다(사실 Objects.hash() 메서드는 각 레벨에서 새로운 어레이를 작성하기 때문에 상속이 있는 경우 오버헤드는 더욱 커집니다).

사용 예:

public int hashCode() {
    return HashCode.hash(HashCode.hash(timestampMillis), name, dateOfBirth); // timestampMillis is long
}

public int hashCode() {
    return HashCode.hash(super.hashCode(), occupation, children);
}

HashCode 도우미:

public class HashCode {

    public static int hash(Object o1, Object o2) {
        return add(Objects.hashCode(o1), o2);
    }

    public static int hash(Object o1, Object o2, Object o3) {
        return hash(Objects.hashCode(o1), o2, o3);
    }

    ...

    public static int hash(Object o1, Object o2, ..., Object o10) {
        return hash(Objects.hashCode(o1), o2, o3, ..., o10);
    }

    public static int hash(int initial, Object o1, Object o2) {
        return add(add(initial, o1), o2);
    }

    ...

    public static int hash(int initial, Object o1, Object o2, ... Object o10) {
        return add(... add(add(add(initial, o1), o2), o3) ..., o10);
    }

    public static int hash(long value) {
        return (int) (value ^ (value >>> 32));
    }

    public static int hash(int initial, long value) {
        return add(initial, hash(value));
    }

    private static int add(int accumulator, Object o) {
        return 31 * accumulator + Objects.hashCode(o);
    }
}

도메인 모델에서 10이 가장 합리적인 속성 수라는 것을 알게 되었습니다.더 많은 속성이 있다면 String과 프리미티브를 더 많이 유지하는 대신 리팩터링과 더 많은 클래스를 도입하는 것을 고려해야 합니다.

단점은 주로 깊은 해시를 필요로 하는 원시 및/또는 배열이 있는 경우 유용하지 않다는 것입니다(보통 이는 사용자가 제어할 수 없는 플랫(전송) 개체를 처리해야 하는 경우입니다).

언급URL : https://stackoverflow.com/questions/5038204/apache-commons-equals-hashcode-builder

반응형