자바 HashSet 클래스 | 자바 입문강좌 46

자바 HashSet 클래스

HashSet은 집합 SET 의 자료구조입니다. 집합은 중복된 요소를 갖지 않는 특성이 있는데 HashSet 도 중복되지 않도록 사용할 수 있습니다.

또 Hash 는 일반적으로 해시함수를 의미합니다. 해시는 랜덤한 길이의 데이터를 고정된 데이터 길이로 매핑하는 함수로 예를 들어 10Byte 문자열을 32비트 해시 값으로 매칭시킬 수 있습니다. 랜덤한 길이의 데이터를 입력(input)이라고 하면 고정된 길이의 데이터는 출력(output)입니다. 입력에 따라 출력이 나오고 출력을 안다고 해서 입력을 알수 없으므로 Hash는 암호화(encryption)에 사용되는 기술입니다.

Hash를 쓰면서 유명해진 것에는 블록체인 디자인 패턴이 있고 비트코인이 있죠. 암호화폐 (Cryptocurrency)에서 암호화라는 것은 Hash (SHA256)를 의미합니다.

SHA256는 (Secure Hash Algorithm 256) 미국국가안보국(NSA)가 설계해서보안성이 매우 높습니다. 특히 SHA256은 한번 암호화 시키면 복호화(원래 키)가 사실상 불가능합니다.

Hash 라는 용어가 많이 나오는데 조금 고급 주제를 다루다 보면 자주 보게 될 단어입니다.

컬렉션 프레임워크에서 나오는 HashSet 은 순차적이지 않은 유일한 요소를 저장할 필요가 있을 때 사용합니다.

우선 아래의 예제를 보겠습니다.

HashSet 예제 1

hs1 인스턴스는 요일 정보를 스트링으로 저장합니다. Sunday 를 두번 입력해봐도 하나밖에 안들어갑니다.

이유는 SET(집합) 의 특징을 가지고 있기 때문입니다. 입력과 삭제, 메소드도 다 있습니다.

import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        HashSet<String> hs1 = new HashSet<>();

        hs1.add("Monday");
        hs1.add("Tuesday");
        hs1.add("Wednesday");
        hs1.add("Thursday");
        hs1.add("Friday");
        hs1.add("Saturday");
        hs1.add("Sunday");

//        겹치지 않는다 SET의 특징
        hs1.add("Sunday");

        System.out.println(hs1);

//        삭제
        hs1.remove("Friday");
        System.out.println(hs1);

//        존재하는지 확인
        System.out.println(hs1.contains("Wednesday"));

//        모두 삭제
        hs1.clear();
        System.out.println(hs1);
    }
}
[Monday, Thursday, Friday, Sunday, Wednesday, Tuesday, Saturday]
[Monday, Thursday, Sunday, Wednesday, Tuesday, Saturday]
true
[]

HashSet 예제 2

다음은 사용자 정의 자료형을 사용한 HashSet 사용입니다. 이제 컬렉션 프레임워크에 익숙해졌다면 제네릭과 다형성의 사용은 어렵지 않을 것 입니다. 이해도가 중요하지만 테크닉적인 부분도 중요하기 때문에 코드를 여러번 짜다보면 자연스럽게 적응이 됩니다.

또 아래에서는 Iterator를 사용하고 있습니다. HashSet 은 SET 이라 인덱스가 없습니다. ArrayList 는 인덱스가 있습니다. 인덱스가 있는 것들은 Sequence (순차)라고 부릅니다. 순차적이라는 것은 시작점이 있고 끝이 있는데 그 사이의 요소들은 어떤 순서를 가지고 인접해 있다는 뜻 입니다.

그냥 SET 은 Sequence 가 아니기 때문에 어떤 식으로 정렬될지 알수 없지만 각각의 요소가 유일(Unique)하다는 특징이 있습니다. 초보자들에게는 요런 차이들이 빨리 익숙하지 않을텐데 충분히 시간을 들여서 익숙해져야 하는 아이디어입니다.

자바에서만 쓰이는게 아니라 대부분 현대 언어에는 Sequence Unique Map 같은 방식을 사용하고 있습니다. 프로그래밍 언어는 여러개를 배우면 많이 알게 되지만 한개를 최종적으로 파다보면 나머지 언어가 사용하는 상당수 개념도 자동적으로 습득하게 됩니다. 온라인게임을 비유하면 자동 스킬(Passive Skill)이 붙는 것과 같습니다.

역시 자동스킬을 얻기 위한 가장 좋은 언어는 C++ 이라고 생각합니다만 C++을 배우려면 다른 언어보다 더 많은 시간과 노력이 필요합니다. 그에 비해 자바는 여러모로 가성비가 좋은 언어입니다. 그래서 많은 사람들이 아직도 자바를 첫번째 언어로 선택하고 있습니다.

package com.kay;


import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;

public class Main {
    public static void main(String[] args) {

        HashSet<MyMemo> mySet = new HashSet<>();
        mySet.add(new MyMemo(2001,"about Hash Set"));
        mySet.add(new MyMemo(3002,"It actually has no order"));
        mySet.add(new MyMemo(2005,"and elements are unique"));
        mySet.add(new MyMemo(2010,"how do I traverse them"));
//        중복 테스트
        mySet.add(new MyMemo(2010,"how do I traverse them"));
        mySet.add(new MyMemo(2021,"you can use iterator"));

        Iterator<MyMemo> itr = mySet.iterator();

        while(itr.hasNext()){
            itr.next().showMemo();
        }

    }
}
class MyMemo{
    private int memoId;
    private String memoText;

    public MyMemo(int memoId, String memoText) {
        this.memoId = memoId;
        this.memoText = memoText;
    }
    public void showMemo(){
        System.out.print("[instance] = " + this.toString() + ", ");
        System.out.print("[Id] " + memoId + ", ");
        System.out.print("[Text] \'" + memoText + "\'");
        System.out.println();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyMemo myMemo = (MyMemo) o;
        return memoId == myMemo.memoId &&
                Objects.equals(memoText, myMemo.memoText);
    }

    @Override
    public int hashCode() {
        return Objects.hash(memoId, memoText);
    }
}
[instance] = com.kay.MyMemo@819c598c, [Id] 3002, [Text] 'It actually has no order'
[instance] = com.kay.MyMemo@5071b394, [Id] 2021, [Text] 'you can use iterator'
[instance] = com.kay.MyMemo@5c9304a9, [Id] 2005, [Text] 'and elements are unique'
[instance] = com.kay.MyMemo@6597bd5, [Id] 2010, [Text] 'how do I traverse them'
[instance] = com.kay.MyMemo@8c2c1f93, [Id] 2001, [Text] 'about Hash Set'

당연하지만 자료구조에 Iterator 를 사용할 수 있다면 검색도 가능합니다. while 문안에 적당히 if 문을 넣어서 검색하면 됩니다.

마지막으로 사용자 정의 자료형 같은 경우 그냥 사용하면 중복 문제가 생깁니다. 중복처리에 관하여 Object 코드의 equals 와 hashCode 를 사용하기 때문인데요. 오버라이드 해줘야 합니다. 위의 코드는 인텔리제이에서 자동생성한 오버라이드 코드입니다.

hashCode 는 사용자 정의 클래스인 MyMemo의 멤버를 가지고 해시코드를 생성합니다. 여기서는 int 형 정수와 String 형을 합친 해시코드입니다. 예를 들어 중복테스트에 사용할 해시값은 6597BD5 입니다. 인스턴스가 달라도 내용물을 같기 때문에 중복검사에 걸립니다.

equals는 HashCode 에서 중복된 객체를 기존의 Object (MyMemo의 인스턴스)와 비교합니다. return 문에서 보면 기본자료형인 정수형은 그냥 == 로 비교해주고 String 클래스에 대해서는 equals 메소드로 비교합니다. 둘다 일치하는 경우 중복이라 판단합니다. 만약 id만 중복을 방지하고 싶다. 그러면 hashCode 와 equals 부분에서 MemoText 부분을 제거하면 됩니다.

약간 복잡해보이지만 포인트는 중복체크를 하기위해 hashCode와 equals 를 사용한다는 것 입니다.

요약

자바 HashSet 클래스의 사용법에 대하여 알아봤습니다. 컬렉션 프레임워크의 자료를 사용하는 것은 기본적으로 삽입, 검색, 삭제, 출력 기능을 수행하는 것 입니다. 그러기 위한 메소드들이 뭐가 있나 어떻게 사용하는가 정도를 이해한 후에 JDK Documentation 에 들어가 보면 각종 잘잘한 메소드들이 있습니다. 자주 쓰지 않지만 꼭 필요한 메소드들은 한번 쓰윽 흝어라도 보면 좋습니다. 나중에 문제를 해결하기 위해 다시 찾아볼 수 있는 참조(reference)문서들은 최대한 많이 읽어두고 즐겨찾기에 저장해두면 시간을 절약할 수 있습니다.

참고문서

Java HashSet (w3schools.com)

HashSet in Java – javatpoint

자바 HashSet 클래스 – GeeksforGeeks

A Guide to HashSet in Java | Baeldung

Java – The HashSet Class – Tutorialspoint

introductory Java HashSet tutorial (zetcode.com)

자바 HashSet 클래스 | 컬렉션 프레임워크

SHA256 Hash Generator Online (passwordsgenerator.net)

Leave a Comment