HashSet
HashSet은 Set인터페이스를 구현한 가장 대표적인 컬렉션이다.
다시 상기하자면, Set은
- 중복 허용 X
- 순차 저장 X
중복된 데이터를 저장하려고 하면 false를 반환한다. HashSet의 특징을 이용하면 중복을 쉽게 제거할 수 있다.
HashSet의 메서드
method | 설명 |
---|---|
HashSet() | (생성자)객체 생성 |
HashSet(Collection c) | 지정한 컬렉션을 포함하는 객체 생성 |
HashSet(int initialCapacity) | 지정한 값을 초기용량으로 하는 객체 생성 |
HashSet(int initialCapacity, float loadFactor) | 초기 용량과 load factor를 지정하는 생성자 |
boolean add(Object o) | 객체 저장 |
boolean addAll(Collection c) | 지정한 컬렉션에 저장된 모든 객체 저장 |
void clear() | 모든 객체 삭제 |
Object clone() | 복제해서 반환(얕은 복사) |
boolean contains(Object o) | 지정한 객체를 포함하고 있는지 알려준다. |
boolean containsAll(Collection c) | 지정한 컬렉션에 저장된 모든 객체를 포함하고 있는지 알려준다. |
boolean isEmpty() | 비어있는지 확인 |
Iterator iterartor() | Iterator반환 |
boolean remove(Object o) | 지정한 객체 삭제 |
boolean removeAll(Collection c) | 지정한 컬렉션에 저장된 객체와 동일한 것을 모두 삭제 |
boolean retainAll(Collection c) | 지정한 컬렉션에 저장된 객체와 동일한 것만 남기고 모두 삭제 |
int size() | 객체의 개수 반환 |
Object[] toArray() | 지정한 객체를 객체 배열 형태로 반환 |
Object[] toArray(Object[] a) | 지정한 객체를 주어진 객체배열a에 담는다. |
load factor는 저장공간이 가득 차기 전에 미리 용량을 확보하기 위한 것이다. 기본값은 0.75. 75% 채워지면 용량이 두 배로 늘어난다.
package com.javaex.ch11;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Bingo {
public static void main(String[] args) {
Set set = new HashSet();
int[][] board = new int[5][5];
for(int i = 0; set.size()<25;i++){
set.add((int)(Math.random()*50)+1+"");
}
Iterator iterator = set.iterator();
for(int i=0; i<board.length;i++){
for(int j=0; j<board[i].length;j++) {
board[i][j] = Integer.parseInt((String)iterator.next());
System.out.print((board[i][j] < 10 ? " ":" ")+board[i][j]);
}
System.out.println();
}
}
}
이 예제를 실행하면 같은 숫자가 비슷한 위치에 나온다는 사실을 발견할 수 있다. HashSet은 순서를 보장하지 않고 자체적인 저장방식에 따라 순서가 결정된다. 이런 경우 HashSet보다 LinkedHashSet을 쓰는 것이 더 나은 선택이다.
package com.javaex.ch11;
import java.util.HashSet;
public class HashSetEx3 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add("abc");
set.add("abc");
set.add(new Person("coco", 20));
set.add(new Person("coco", 20));
System.out.println(set);
}
}
class Person {
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " : " + age;
}
}
/*
결과 :
[abc, coco : 20, coco : 20]
*/
위 예제는 이름과 나이가 같으면 같은 사람으로 인식하려는 의도로 작성했다. 그러나 의도와 달리 결과는 두 개의 Person객체가 출력된다.
HashSet의 add()는 새로운 요소를 추가하기 전 기존 요소와 같은 것인지 판별하기 위해 추가하려는 요소의 equals()와 hashCode()를 호출하므로 두 메서드를 목적에 맞게 오버라이딩 할 필요가 있다.
package com.javaex.ch11;
import java.util.HashSet;
public class HashSetEx3 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add(new Person("coco", 20));
set.add(new Person("coco", 20));
System.out.println(set);
}
}
class Person {
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Person) {
Person person = (Person)obj;
return name.equals(person.name) && age == person.age;
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return name + " : " + age;
}
}
hashCode()를 오버라이딩할 때 다음 세 가지 조건을 만족시켜야 한다.
실행 중인 애플리케이션 내 동일한 객체에 대해 여러 번 hashCode()를 호출해도 동일한 int값을 반환해야 한다. ( 실행할 때마다 동일한 값을 반환할 필요는 없다.)
equals()를 이용해 true를 얻은 두 객체에 대해 각각 hashCode()를 호출해서 얻은 결과는 반드시 같아야 한다.
- equals()를 호출했을 때 false를 반환하는 두 객체는 hashCode() 호출에 대해 같은 int를 반환하는 경우가 있어도 괜찮다. 하지만 해싱hassing을 사용하는 컬렉션 성능을 향상시키기 위해서는 다른 int값을 반환하는 것이 좋다.