얼마 전, 프리온보딩 Kotlin 과정을 신청했는데 사전과제 중 하나가 List, Set, Map에 대해서 설명하는 것이었다. 나는 코딩테스트를 풀 때 HashSet을 굉장히 많이 사용하는 편이라 Set에 대해서 자세히 알고있다고 생각했는데, 적다보니 막상 몇 줄 못적겠더라... 그래서 Set에 대해 한 번 정리하는 기회가 필요하다고 느꼈다.
Set
Set은 데이터의 집합이며, List와 다르게 순서가 보장되지 않고, 데이터의 중복값을 가질 수 없다.
Set 인터페이스를 구현한 클래스로는 HashSet, LinkedHashSet, TreeSet이 존재한다.
HashSet
- 인스턴스의 해시값을 기준으로 저장하기 때문에 순서를 보장하지 않는다.
- Null 값을 허용한다.
- 중복된 값을 허용하지 않는 특징이 있기 때문에, 값의 존재 유무를 파악할 때 유용하게 사용할 수 있다.
그럼 중복된 값은 어떻게 걸러낼 수 있을까?
HashSet은 객체를 저장하기 전에 먼저 객체의 hashCode() 메소드를 호출해서 해시 코드를 얻어낸 다음, 저장되어있는 객체들의 해시코드와 비교한 뒤, 같은 해시 코드가 있다면 equals() 메소드로 두 객체를 비교해서 true(두 객체가 동일한 객체)가 나오면 저장하지 않는다.
해시코드란 ?
객체를 식별하는 하나의 정수값을 말한다. Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체 마다 다른 값을 가지고 있다.
문자열을 HashSet에 저장할 경우, 같은 문자를 갖는 String 객체는 동일한 객체로 간주되고, 다른 문자열을 갖는 String 객체는 다른 객체로 간주된다 (String 클래스가 hashCode()와 equals()메소드를 재정의해서 같은 문자열일 경우 리턴 값을 동일한 객체로 판단하도록 했기 때문).
HashSet 선언 방법
HashSet<String> set1 = new HashSet<>();
HashSet<String> set2 = new HashSet<String>(); // 타입 지정
HashSet<String> set3 = new HashSet<>(10); // 초기 용량 세팅
HashSet<String> set4 = new HashSet<>(colors1); // 다른 Collection 값으로 초기화
HashSet<String> set5 = new HashSet<>(Arrays.asList("A", "B", "C")); // 초기 값 세팅
HashSet<String> set6 = new HashSet<>(){{
add("A");
add("B");
add("C");
}}; // 값을 추가하면서 생성 가능
HashSet 값 추가
add(Object): HashSet 의 마지막에 데이터 추가
HashSet<String> set = new HashSet<>();
set.add("apple"); // 추가
set.add("mango");
set.add("banana");
// [banana, apple, mango]
set 을 출력시, 순서가 추가한 순이 아닌 것을 알 수 있다. 이는 입력된 순서가 보장되지 않기 때문이다. 또한 출력할 때 마다 순서가 바뀌기도 한다.
HashSet 값 삭제
remove(Object): 해당 Object 와 같은 값 삭제
clear(): 데이터 모두 삭제
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C", "D"));
// [A, B, C, D]
set.remove("C"); // [A, B, D]
set.remove("A"); // [B, D]
set.clear(); // []
HashSet 전체 값 출력
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C", "D"));
// for-each loop
for (String alphabet : set) {
System.out.print(alphabet + " ");
}
System.out.println(); // A B C D
// using iterator
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println(); // A B C D
HashSet 값 존재 유무
HashSet은 값 존재 여부를 확인하는데 최적화된 자료 구조이다!
contains(Object): 해당 Object 가 존재 여부를 boolean 타입으로 리턴한다.
HashSet<String> set = new HashSet<>(Arrays.asList("A", "B", "C", "D"));
set.contains("A"); // true
set.contains("E"); // false
HashSet 크기
size(): HashSet의 크기를 구할 수 있다.
HashSet<Integer> set = new HashSet<Integer>(); // Integer
set.add(1);
set.add(2);
set.add(3);
set.add(1);
set.size(); // 3
HashSet<String> set2 = new HashSet<String>(); // String
set2.add("a");
set2.add("b");
set2.add("c");
set2.add("a");
set2.add("b");
set2.size(); // 3
위에서 언급한 것 처럼, HashSet은 중복값을 허용하지 않는다.
따라서 위의 경우 중복된 값은 추가되지 않아, 사이즈가 3으로 나오는 것을 볼 수 있다.
HashSet은 정렬을 지원하지 않는다고 한다. 그런데... 출력 결과를 보면 정렬이 되는 경우가 상당히 많다. HashSet은 정말 정렬을 지원하지 않는 것일까? 그러면 한 번쯤은 랜덤하게 배열이 나와야하는 것이 아닐까?
HashSet<String> set = new HashSet<>(Arrays.asList("A", "C", "B", "D"));
위 처럼 ABCD 가 아닌 ACBD로 하고 출력해도 ABCD 순서로 출력이 된다. 이게 과연 우연일까?
이에 대해서는 LinkedHashSet을 다루면서 더 알아보도록 하겠다!
[참고]
https://cocoon1787.tistory.com/527
'𝑷𝒓𝒐𝒈𝒓𝒂𝒎𝒎𝒊𝒏𝒈 > 𝐽𝐴𝑉𝐴' 카테고리의 다른 글
[JAVA] Collection - Set (LinkedHashSet + HashSet) (0) | 2022.11.24 |
---|---|
[JAVA] Collection - Set (SortedSet, TreeSet) (0) | 2022.11.24 |
[JAVA] Collection - List (Vector) (0) | 2022.11.22 |
[JAVA] Collection - List (LinkedList) (0) | 2022.11.21 |
[JAVA] Collection - List (ArrayList) (0) | 2022.11.21 |