본문 바로가기

회사/청소년

객체 비교에 있어 equals와 hashcode

package kr.go.cdc.survey.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import kr.go.cdc.survey.common.validator.MaxByte;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

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

/**
 * 패널 엔티티
 *
 * @author : jkkim
 */

@Getter
@Setter 
public class PnlEntity {

    /**생년월일*/
    @MaxByte(8)
    private String brthdy;
    /**패널성명*/
    @NotEmpty
    @MaxByte(50)
    private String pnlNm;
    /**패널번호*/
    private String pnlId;
    /**시군구명*/
    private String signguNm;
    /**시도명*/
    private String ctprvnNm;
    /**패널KEY*/
    private String pnlKey;
    /**참여번호*/
    private String partcptnNo;
    /**신분유형코드*/
    @NotEmpty
    private String sclpstTyCode;
    /**보호자패널관계설명*/
    private String prtctorPnlRelateDc;
    private String prtctorPnlRelateDc2;
    /**삭제여부*/
    private String deleteAt;
    /**보호자내선번호*/
    private int prtctorLxtnNo;
    /**최종수정자KEY*/
    private String lastUpdusrKey;
    /**최종수정자명*/
    private String lastUpdusrNm;
    /**최종수정일시*/
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
    private Date lastUpdDt;
    /**등록자KEY*/
    private String registerKey;
    /**등록자명*/
    private String registerNm;
    /**등록일시*/
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
    private Date registDt;
    /**특이사항*/
    private String partclrMatter;
    /**만나이*/
    private String age;
    /**보호자 만나이*/
    private String prtctorAge;
    /**보호자2 만나이*/
    private String prtctorAge2;
    /**보호자우편번호*/
    @NotEmpty
    @MaxByte(7)
    private String prtctorZip;
    /**보호자주소*/
    @NotEmpty
    @MaxByte(250)
    private String prtctorAdres;
    /**보호자전화번호*/
    @MaxByte(20)
    private String prtctorTelno;
    @MaxByte(20)
    private String prtctorTelno2;
    /**보호자핸드폰번호*/
    @NotEmpty
    @MaxByte(20)
    private String prtctorMbtlnum;
    @MaxByte(20)
    private String prtctorMbtlnum2;
    /**보호자생년월일*/
    private String prtctorBrthdy;
    /**보호자2 생년월일*/
    private String prtctorBrthdy2;
    /**보호자명*/
    @NotEmpty
    @MaxByte(50)
    private String prtctorNm;
    @MaxByte(50)
    private String prtctorNm2;
    /**보호자이메일*/
    @Email
    private String prtctorEmail;
    /**보호자패널관계코드*/
    @NotEmpty
    private String prtctorPnlRelateCode;
    private String prtctorPnlRelateCode2;
    /**우편번호*/
    @NotEmpty
    @MaxByte(7)
    private String zip;
    /**주소*/
    @NotEmpty
    @MaxByte(250)
    private String adres;
    /**이메일*/
    @Email
    private String email;
    /**반*/
    private String clas;
    /**학년*/
    private String grade;
    /**학교명*/
    private String schulNm;
    /**전화번호*/
    private String telno;
    /**휴대폰번호*/
    @MaxByte(20)
    private String mbtlnum;
    /**패널유형코드*/
    @NotEmpty
    private String pnlTyCode;
    /**성별코드*/
    private String sexdstnCode;
    /**변경사유*/
    @MaxByte(4000)
    private String updResn;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PnlEntity)) return false;
        PnlEntity pnlEntity = (PnlEntity) o;
        return Objects.equals(brthdy, pnlEntity.brthdy) &&
                Objects.equals(pnlNm, pnlEntity.pnlNm) &&
                Objects.equals(pnlId, pnlEntity.pnlId) &&
                Objects.equals(sclpstTyCode, pnlEntity.sclpstTyCode) &&
                Objects.equals(prtctorPnlRelateDc, pnlEntity.prtctorPnlRelateDc) &&
                Objects.equals(prtctorAdres, pnlEntity.prtctorAdres) &&
                Objects.equals(prtctorTelno, pnlEntity.prtctorTelno) &&
                Objects.equals(prtctorMbtlnum, pnlEntity.prtctorMbtlnum) &&
                Objects.equals(prtctorBrthdy, pnlEntity.prtctorBrthdy) &&
                Objects.equals(prtctorNm, pnlEntity.prtctorNm) &&
                Objects.equals(prtctorEmail, pnlEntity.prtctorEmail) &&
                Objects.equals(prtctorPnlRelateCode, pnlEntity.prtctorPnlRelateCode) &&
                Objects.equals(adres, pnlEntity.adres) &&
                Objects.equals(email, pnlEntity.email) &&
                Objects.equals(clas, pnlEntity.clas) &&
                Objects.equals(grade, pnlEntity.grade) &&
                Objects.equals(schulNm, pnlEntity.schulNm) &&
                Objects.equals(telno, pnlEntity.telno) &&
                Objects.equals(mbtlnum, pnlEntity.mbtlnum) &&
                Objects.equals(pnlTyCode, pnlEntity.pnlTyCode) &&
                Objects.equals(sexdstnCode, pnlEntity.sexdstnCode) &&
                Objects.equals(prtctorPnlRelateDc2, pnlEntity.prtctorPnlRelateDc2) &&
                Objects.equals(prtctorPnlRelateCode2, pnlEntity.prtctorPnlRelateCode2) &&
                Objects.equals(prtctorTelno2, pnlEntity.prtctorTelno2) &&
                Objects.equals(prtctorMbtlnum2, pnlEntity.prtctorMbtlnum2) &&
                Objects.equals(prtctorNm2, pnlEntity.prtctorNm2) &&
                Objects.equals(prtctorBrthdy2, pnlEntity.prtctorBrthdy2);
    }

    @Override
    public int hashCode() {
        return Objects.hash(brthdy, pnlNm, pnlId, sclpstTyCode, prtctorPnlRelateDc, prtctorAdres, prtctorTelno, prtctorMbtlnum, prtctorBrthdy, prtctorNm, prtctorEmail, prtctorPnlRelateCode, adres, email, clas, grade, schulNm, telno, mbtlnum, pnlTyCode, sexdstnCode);
    }
}

 

    /**
     * 패널정보 기본정보 변경이력 목록 조회
     *
     * @param pnlInfoMngVo
     * @return
     */
    public List<PnlInfoMngDto> selectPnlChghstBasicInfoGroupList(PnlInfoMngVo pnlInfoMngVo) {
        //이력 목록
        List<PnlInfoMngDto> pnlChghstBasicInfoList = pnlInfoMngDao.selectPnlChghstBasicInfoGroupList(pnlInfoMngVo);
        //이력 그룹 목록
        List<PnlInfoMngDto> pnlChghstBasicInfoGroupList = new ArrayList<>();

        for(int i=0; i<pnlChghstBasicInfoList.size(); i++){
            if(i == 0){
                pnlChghstBasicInfoGroupList.add(pnlChghstBasicInfoList.get(i));
            } else {
                PnlInfoMngDto preData = pnlChghstBasicInfoList.get(i-1);
                PnlInfoMngDto currData = pnlChghstBasicInfoList.get(i);
                if(!preData.equals(currData)){
                    pnlChghstBasicInfoGroupList.add(currData);
                }
            }
        }
        //역순 정렬
        Collections.reverse(pnlChghstBasicInfoGroupList);
        return pnlChghstBasicInfoGroupList;
    }

 

패널정보 기본정보 변경이력 목록 조회 할 시

이력목록에서 PnlInfoMngDto를 equals로 비교해서 동일하면 이력그룹목록에 추가 안하고 있다.

 

자바에서는 두 객체를 동등비교 할 때 equals()메소드를 먼저 사용한다.
예외) hashCode()메소드를 먼저 사용해서 동등비교는 Collection에서 hash값을 사용하는 HashSet, HashMap, HashTable을 사용할 때
 -> 뒤에서 hashCode 설명 할 때 자세히 

Object클래스의 equals()는 
public boolean equals(Object obj) {
    return (this == obj);
}
오로지 참조값(객체의 주소값)이 같은지로 동일 객체인지를 확인한다.
객체의 필드의 값이 같아도, 인스턴스가 다르니 객체는 다른 주소를 가지고 있기 때문에 Object.equals()는 false로 나온다. 

 

같은 클래스의 객체면서 논리적으로 동등하다면(ex)필드값이 동일하다면) true라고 리턴하고 싶다.
String class는 equals() 메소드를 재정의해서 번지비교가 아닌 문자열 '값'을 비교하고 있다.
이처럼 논리적으로 동등하게 비교하고 싶은 클래스에 equals()를 재정의해주면 된다.
intellij의 generate 기능을 쓰면 쉽게 equals()를 재정의 할 수 있다.
위의 코드 참고

Java hashCode란
객체 해시코드란 객체를 식별하는 하나의 정수값을 말한다.
Object의 hashCode()메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에
객체 마다 다른값을 가지고 있다.
객체의 값을 동등비교시 hashCode()를 오버라이딩 해야 한다.

컬렉션 프레임워크에서 HashSet, HashMap, HashTable은 hashCode()를 먼저 보고, 다음으로 equals()로 본다.
1) hashCode() : 메소드를 실행해서 리턴된 해시코드 값이 같은지를 본다. 해시 코드값이 다르면 다른 객체로 판단
hashCode와 equals가 다 맞아야 동등객체로 판단한다. 

만약 hashCode()값이 다르면 equals()는 비교도 하지 않는다.

equals()와 hashCode()를 같이 재정의해야 하는 이유
equals()만 재정의 한다면 hash를 사용하는 HashSet, HashMap, HashTable에서 
객체의 값이 같아도 동등비교가 false가 나올 것이다.

결론 : Object의 equals()와 hashCode() 둘 다 재정의해서 쓰도록 하자