개발/JAVA

Hash Set - Custom class 중복 제거하기

신매력 2014. 2. 16. 17:00

Java의 콜렉션 중 Set은 중복된 값이 들어가지 않는다.

어릴적 배웠었던 수학에서의 집합의 특성과 같다.

'여러대상의 모임이며, 순서가 없고 중복된 값이 없다'


Set의 구현체 중 HashSet을 이용해보겠다.

아래처럼 사용한다.


Test.java

public void 해시셋테스트() {

  Set<Integer> set = new HashSet<Integer>();

  set.add(1);

  set.add(2);

  set.add(2);

  System.out.println(set);

} 


결과 >>

 [1, 2]



결과를 보면, 중복된 값을 제거해서 저장해준다.


그러나, Wrapper class(Integer, String, Long...)가 아닌

내가 만든 Custom class로 하면, 중복된 데이터가 들어가버린다.

아래의 예를 한번 보자.


CreateObject.java

public class CreateObject {

  public Integer a;

  public Integer b;

  public CreateObject(Integer a, Integer b) {

    this.= a;

    this.= b;

  }



Test.java

public void 해시셋테스트() {

  Set<CreateObject> set = new HashSet<CreateObject>();

  CreateObject obj1 = new CreateObject(1, 2);

  set.add(obj1);

  CreateObject obj2 = new CreateObject(1, 2);

  set.add(obj2);


  System.out.println(set);

}


결과 >>

[CreateObject@5710fdb4, CreateObject@103ff589]



obj에 들어있는 값이 서로 완전히 같은데도 불구하고,

둘은 다르다고 판단되어 Set에 따로따로 들어가버렸다.

 


왜 이런일이 발생하는 것인가?



그 이유는, HashSet이 내부적으로 해당 객체의 hashCode()와 equals()를 실행해보기 때문이다.


먼저 hashCode()메소드를 호출해서 hashCode가 같은지 판별한다.

만약 hashCode가 같으면 equals 메소드를 실행해보고 같은지 판별한다.

만약 같지 않으면, 두 객체는 같지 않은것이므로 equals메소드를 실행하지 않는다.



위의 예제에서 내가 만든 커스텀 클래스인 CreateObject 의 hashCode와 equlas메소드는
자바의 가장 상위객체인 Object.class의 메소드를 실행한다.
Object.java의 hashCode메소드는 JNI를 부르는데, C에서 알아서 조합해서 만들어준다고 한다.


반면 잘 중복제거를 해줬던 Wrapper Class인 Integer.class의 hashCode메소드를 보니

override를 해서 value를 리턴하고 있다.


우리도 래퍼클래스처럼 Object.class의 hashCode(), equals 메소드를 사용할 것이 아니라,

오버라이드를 해줘야하는 것이다.



구현하기 나름이지만 예제를 한번 만들어보았다.


CreateObject.java

public class CreateObject {

  public Integer a;

  public Integer b;

  public CreateObject(Integer a, Integer b) {

    this.a = a;

    this.b = b;

  }


  @Override

  public int hashCode() {

    return new HashCodeBuilder().append(a).append(b).toHashCode();

  }


  @Override

  public boolean equals(Object obj) {

    CreateObject rhs = (CreateObject) obj;

    if (obj instanceof CreateObject) {

      return new EqualsBuilder().append(a, rhs.a).append(b, rhs.b).isEquals();

    }

    return false;

  }

}



hashCode메소드에서는 a, b 값으로 해시코드를 생성해서 리턴을 해주고,

equals메소드에서는 객체에 들어있는 모든 값이 같은지 판별해서 리턴한다.


참고로 Builder 클래스들은 common lang 라이브러리에 들어있다.

꼭 저렇게 안해도되고, 그냥 슈퍼클래스의 equals를 써도 상관 없다.




이렇게 구현한 후 다시 테스트코드를 실행시켜보니

중복된 데이터는 제거되어 들어갔다~