╱╱╭╮╱╱╱╱╱╱╭━━━╮╱╱╱╭╮╱╭╮╱╱╱╱╱╱ ╱╱┃┃╱╱╱╱╱╱┃╭━╮┃╱╱╱┃┃╱┃┃╱╱╱╱╱╱ ╱╱┃┣━━┳━━╮┃┃╱┃┣━╮╱┃╰━╯┣━━┳━╮╱ ╭╮┃┃╭╮┃┃━┫┃╰━╯┃╭╮╮┃╭━╮┃╭╮┃╭╮╮ ┃╰╯┃╭╮┃┃━┫┃╭━╮┃┃┃┃┃┃╱┃┃╭╮┃┃┃┃ ╰━━┻╯╰┻━━╯╰╯╱╰┻╯╰╯╰╯╱╰┻╯╰┻╯╰╯

Java/Java 기초

[Java] 컬렉션 프레임워크 Set (HashSet)

재안안 2022. 4. 28. 22:50

컬렉션 프레임워크(collection framework)란 다수의 데이터들을 쉽게 다루기위해 만들어진 클래스들의 집합체이다.

 

컬렉션 프레임워크에서 제공하는 클래스들은 수많은 사람들을 통해 표준화되었고 이로인해 각 상황에 맞춰서 편하게 사용만 하면 된다.

 

컬렉션 프레임워크에서 제공되는 클래스들은 인터페이스의 형태로 만들어져 있다.

사용시 클래스로 구현해야 한다.

 

자바 컬렉션 프레임워크는 크게 세단위로 나눌 수 있다.

 

  • List<E>
  • Set<E>
  • Map<K,V>

 

컬렉션 프레임에 대해 바로 알아보기전에 먼저 알아야하는 개념이 있다.

 

바로 컬렉션 프레임워크의 관계이다.

 

컬렉션 프레임워크 인터페이스 상속관계

 

저번에도 말했듯이 list와 set에서 쓰는 메소드들은 대부분 collection에 저장되어 있고

이는 사용하는 메소드가 비슷하다는 것을 뜻한다.

 

  • Set<E>

 

이번엔 Set<E>에 대해서 알아볼 것이다.

 

Set<E>을 구현한 클래스도 데이터를 저장하는 목적으로 쓰인다.

 

Set<E>을 구현한 클래스들은 아래와 같은 특징을 가진다.

 

  • 데이터의 중복을 허용하지 않는다.
  • 데이터의 순서가 없다. (인덱스x)

 

ArrayList는 배열을 떠올리면 이해가 편하지만 Set<E>은 Set<E>자체를 이해해야 한다.

 

Set<E>는 데이터의 중복을 허용하지 않는다.

 

Set<E>을 구현한 클래스가 데이터의 중복을 확인하는 방법은 아래와 같다.

 

add()를 통해 저장하고 싶은 요소가 전달되면 요소를 저장하기전에

 

우선 요소의 hashCode()를 통해 hash code를 얻어내고

얻어낸 요소의 hash code를 set내부 요소들의 hash code와 비교한다. (equals() 사용)

이때 단 하나의 요소에 대한 비교가 true가 나온다면 set은 해당 요소를 저장하지 않는다.

 

그래서 set을 구현한 클래스를 사용하는 경우 hashCode()와 equals()는

이미 override된 상태이니 왠만하면 건들이지 않는 것이 좋다.

 

데이터의 순서가 없는건 Hash의 특성 때문인데 이는 잠시 후에 설명하도록 하겠다.

 

 

아래는 set에서 자주 쓰이는 메소드들이다.

 

메소드 설명
add(E elem) 요소 추가
clear() 모든 요소 제거
contains(Object o) o가 들어있는지 확인
equals(Object o) o와 해당 set이 같은지 확인
isEmpty() 해당 set이 비어있는지 확인
Iterator() 반복객체 반환
remove(Object o) 해당 set에서 o 제거
size() 해당 set의 요소 개수 반환

 

대부분의 메소드들은 List<E>와 같다.

위의 표에서 설명란중에 "~지 확인"이 포함되어 있는 메소드는 boolean 값을 return하는 메소드 들이다.

확인 결과를 반환 한다고 생각하면 좋다.

 

하지만 여기서 제일 중요한건 Iterator()이다.

 

ArrayList의 경우 내부에 저장된 데이터에 대한 접근을 인덱스를 통해 할 수 있었다. get(index), set(index)

 

그러나 set는 저장한 데이터의 순서(인덱스)가 없다고 했다.

set 내부의 데이터에 접근하려면 Iterator()를 통해서만 접근이 가능하다.

 

Iterator()를 통해 임시 객체를 만들어서 임시 객체를 통해 내부 데이터에 간접적으로 접근한다.

이때, 만들어지는 임시 객체는 1회성이다. 다른 작업을 하려면 또 만들어야한다.

 

 

그럼 이제 set<E>를 구현하는 인터페이스 중에서 가장 대표적인 HashSet을 설명하겠다.

 

  • HashSet<E> : Hash algorithm을 이용하는 Set<E>

 

HashSet 클래스를 통해 위 메소드 사용 후 추가적인 설명을하겠다.

 

HashSet 클래스 사용예제

  • HashSet 클래스의 메소드들을 사용하였다.
  • iterator()를 통해 데이터에 접근하였다.
  • iter는 1회성이라 다시 사용하기 위해 재생성 하였다.

 

그리고 HashSet과 Iterator를 사용하려면 java.util에서 import해와야한다.

 

아래의 실행결과를 통해 메소드들이 문제 없이 실행된 것을 알 수 있다.

 

실행 결과

 

HashSet 클래스는 사실 클래스 내부의 배열을 통해 데이터를 저장한다.

 

배열을 통해 데이터를 저장하는데 순서가 없다는게 이유는 Hash 때문이다.

 

데이터를 배열에 저장하긴하는데 연속된 주소를 통해 인덱스를 나누지 않는다.

 

데이터는 Hash function을 통해 고유한 숫자를 얻게되고 이를 인덱스로 사용하는 것이다.

 

저번 시간에 알아봤던 LinkedList를 생각하면 이해가 쉬울 것이다.

 

Hash function을 통해 만들어진 숫자와 데이터가 서로 연결돼 있는 것이다. (데이터가 숫자의 꼬리를 물고있다.)

 

이렇게해서 얻는 이점은 아래와 같다.

 

  • 데이터 추가 또는 삭제시 데이터의 이동이 없다.

 

ArrayList의 데이터 처리 속도가 상대적으로 느린 이유가 데이터의 이동 때문이였다.

 

HashSet에선 데이터의 이동이 필요하지 않으면서 내부 배열을 이용하기 때문에 빠른 검색 속도와 빠른 데이터 수정 속도를 갖는 것이다.

 

 

요약하자면 HashSet이 빨라서 좋다는 말이다.