ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 컬렉션 Set - HashSet
    백엔드/자바 2020. 8. 16. 05:55

    HashSet이란?

    Set 인터페이스를 구현한 가장 대표적인 컬렉션이다.

     

    List와 반대로 저장 순서를 보장하지 않고, 중복을 허용하지 않는다.

     

    만약 Set인터페이스에 속한 클래스를 쓰면서 저장 순서를 유지하고 싶다면, LinkedHashSet을 쓰면 된다.

    HashSet과 TreeSet의 차이

    Set 인터페이스에서는 HashSet과 TreeSet이 가장 대표적인 클래스다.

     

    HashSet의 Set 인터페이스의 특성대로 순서를 보장하지 않고, 중복을 허용하지 않는다.

     

    TreeSet은 범위 검색, 정렬에 특화된 클래스다.

     

    같은 자료를 담더라도 TreeSet은 데이터 범위 검색, 정렬에서 장점을 가진다.

     

    하지만 TreeSet은 HashSet에 비해 데이터의 추가, 삭제가 느린 단점이 있다.

     

    그래서 본인의 용도에 맞게 HashSet과 TreeSet 중 골라서 사용하면 된다.

    HashSet 사용법

     

    HashSet은 Set 인터페이스를 상속받았기 때문에 Set의 자료형으로도 사용이 가능하다.

     

     

     

    HashSet을 선언한 뒤에는 위와 같이 add() 메서드를 이용해서 Set자료형에 입력할 수 있다.

     

    앞서 말한대로 Set은 중복을 허용하지 않기에 위 코드처럼 중복된 자료를 입력해도 알아서 거른다.

     

    참고로 위의 코드에서는 결과물이 순서대로 나왔는데, HashSet은 항상 순서를 보장하지 않는다.

    HashSet 정렬하기

     

    public static void main(String[] args) {
    
            // HashSet 선언
            Set hs1 = new HashSet();
    
            for(int i=0; hs1.size() < 6; i++){
                int num = (int)(Math.random()*45) + 1;
                hs1.add(num);
            }
    
            System.out.println(hs1);
        }

     

    위 코드는 로또 번호를 생성하는 코드로, 그러니까 1~45까지 숫자를 랜덤하게 HashSet에 넣는 코드다.

     

    이때 HashSet의 결과물을 보면 당연하게도 각 요소들이 정렬되지 않은 채 저장된 것을 확인할 수 있다.

     

    이전에 이야기했듯 HashSet은 데이터를 중복 없이 순서 없이 저장을 한다.

     

    그렇다면 이 자료들을 정렬할 수는 없을까? 당연히 HashSet의 형태로는 불가하다.

     

    대신 HashSet을 List의 형태로 변환해서 정렬하는 것은 가능하다.

     

     

     

    위의 코드처럼 HashSet의 데이터를 List에 담아준 다음 sort()를 사용해 정렬된 데이터를 얻을 수 있다.

     

    참고로 HashSet의 데이터를 List형태의 데이터에 담을 땐 ArrayList, LinkedList 둘 다 가능하다.

    HashSet의 중복 여부 확인

    이전 단락들에서 계속 언급했듯 HashSet은 중복 데이터를 저장하지 않는다.

     

    우리는 HashSet에 데이터를 입력할 때 add()라는 메서드를 써 줄 뿐,

     

    중복 데이터를 걸러주는 코드는 같은건 쓰지 않는다.

     

    하지만 HashSet은 혼자 알아서 중복을 다 걸러낸다.

     

    그럼 과연 HashSet은 언제 어디서 중복 데이터를 거를까?

     

    아래 코드를 보면서 알아보도록 하자.

     

     

    public class String_test {
    
        public static void main(String[] args) {
    
            // HashSet 선언
            Set hs1 = new HashSet();
            
            hs1.add(new Person("철수", 15));
            hs1.add(new Person("철수", 15));
    
            System.out.println(hs1);
        }
    }
    
    class Person{
        String name;
        int age;
    
        Person(String name, int age){
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString(){
            return name+":"+age;
        }
    }

     

    위의 코드는 Person이라는 클래스를 통해 만든 객체를 HashSet 변수 hs1에 추가해주는 코드다.

     

    과연 HashSet은 중복 데이터를 걸러낼 수 있을까?

     

     

     

    결론은 중복 데이터 제거에 실패했다.

     

    HashSet은 같은 데이터를 가진 객체임에도 불구하고 중복을 걸러내질 못했다.

     

    왜 실패했을까?

     

    우선 이 이유를 알기 위해서는 우리가 HashSet이 어떻게 중복을 거르는지 알아야 한다.

     

    우리는 HashSet에 데이터를 추가할 때 add()메서드를 사용하게 된다.

     

    이 때 아래와 같은 과정을 거치게 된다.

     

     

     

    add() 메서드를 실행하면 equals() 메서드를 실행한다.

     

    그리고 이미 HashSet에 저장된 객체와 입력하는 객체를 비교한다.

     

    만약 equals()의 결과가 true일 때 set 내에 같은 객체가 존재하므로 객체가 저장되지 않는다.

     

    반면 equals()의 결과가 false일 때 같은 set 내에 같은 객체가 존재하지 않는 것이므로 객체가 저장된다.

     

    즉 어떤 클래스의 객체를 만들어서 HashSet에 넣어 중복 제거를 하고 싶다면 equals()를 오버라이딩한다.

     

     

    java.lang 패키지 - Object 클래스

    Object 클래스 모든 클래스의 최고 조상 클래스로 Object 클래스의 모든 멤버는 모든 클래스에서 바로 사용이 가능하다. Object 클래스는 멤버 변수는 존재하지 않고 11개의 메서드만 가지고 있으며 Ob

    sgcomputer.tistory.com

     

    추가로 이전 Object 클래스에서 언급한대로 특정 클래스 안에서 equals()을 오버라이딩했다면,

     

    hashCode()메서드도 오버라이딩을 해야한다는 원칙에 근거해서 hashCode()도 오버라이딩해준다.

     

    HashSet이 본인의 클래스를 제대로 저장하게하려면 equlas(), hashCode()를 오버라이딩해야한다.

     

    그럼 아래와 같이 Person클래스에 equals()와 hashCode()를 오버라이딩한 코드를 추가해보자.

     

     

     

    class Person{
        String name;
        int age;
    
        // 생성자
        Person(String name, int age){
            this.name = name;
            this.age = age;
        }
    
        // toString 오버라이딩
        @Override
        public String toString(){
            return name+":"+age;
        }
    
        // equals 오버라이딩
        @Override
        public boolean equals(Object o) {
    
            if(!(o instanceof Person)){
                return false;
            }
    
            Person m = (Person) o;
    
            if(this.name == m.name && this.age == m.age){
                return true;
            } else{
                return false;
            }
        }
    
        // hashCode 오버라이딩
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }

     

    여기서 equals()를 사용할 땐 꼭 instanceof를 이용해서 객체가 Person 객체가 맞는지 확인해야 한다.

     

    hashCode()를 사용할 때 hash()라는 메서드를 쓰는데 이건 Objects 클래스에서 제공하는 것으로

     

    name, age의 값을 받아서 해시코드를 생성하는 역할을 해준다.

     

     

     

    오버라이딩을 완료하고 다시 한번 메서드를 실행해보면 위와 같이 제대로 비교된 결과물을 얻을 수 있다.

    '백엔드 > 자바' 카테고리의 다른 글

    컬렉션 Map - HashMap  (0) 2020.08.17
    컬렉션 Set - TreeSet  (0) 2020.08.16
    컬렉션 List - Stack & Queue  (0) 2020.08.16
    컬렉션 List - LinkedList  (0) 2020.08.16
    컬렉션 List - ArrayList  (0) 2020.08.16
Designed by Tistory.