HashSet이란?
Set은 중복을 허용하지 않는 자료구조로, 순서대로 입력되지 않고 일정하게 유지되지 않는게 특징이다.
HashSet은 null 요소도 허용한다. 가장 큰 특징은 중복을 허용하지 않는 것.
중복을 걸러내는 과정
HashSet은 객체를 저장하기 전에 먼저 객체의 hashCode() 메소드를 호출하여 해시코드를 얻어 낸 다음 저장되어 있는 개체들의 해시 코드와 비교한 뒤 같은 해시 코드가 있다면 equals() 메소드로 두 객체를 비교하여 같은 객체라면 중복 저장을 하지 않는다.
예제
fun main() {
val hashSet = hashSetOf(1,2,3,4,5)
hashSet.add(-1)
hashSet.add(0)
hashSet.add(-3)
print(hashSet) // [-1, 0, 1, 2, -3, 3, 4, 5]
}
HashSet에서 -1, 0, -3을 요소로 추가했지만 뒤에 붙는 것이 아닌 어떤 것은 앞 쪽에 -3은 중간에 들어갔다.
순서가 보장되지 않는다.
다음으로 LinkedHashSet에 값을 추가하는 예제를 보면
val linkedHashSet = linkedSetOf(1,2,3,4,5)
linkedHashSet.add(-1)
linkedHashSet.add(0)
linkedHashSet.add(-3)
print(linkedHashSet) // [1, 2, 3, 4, 5, -1, 0, -3]
LinkedHashSet에서 -1, 0, -3을 요소로 add 했고, 순서대로 뒤에 붙는 것을 확인할 수 있다.
생성
개요
코틀린에서 Set은 setOf, MutableSet은 mutableSetOf 메소드를 사용해서 생성한다. 하지만, Set과 MutableSet은 인터페이스일 뿐이다. 이 인터페이스를 사용하기 위해서는 setOf나 mutableSetOf가 인터페이스를 구현하는 클래스의 인스턴스를 만들어야 한다.
val set : Set<Int> = setOf(1,2,3)
val mutableSet : MutableSet<Int> = mutableSetOf(1,2,3)
setOf 메소드와 mutableSetOf 메소드의 내부를 살펴보면
public fun <T> setOf(vararg elements: T): Set<T> =
if (elements.size > 0) elements.toSet() else emptySet()
public fun <T> Array<out T>.toSet(): Set<T> {
return when (size) {
0 -> emptySet()
1 -> setOf(this[0])
else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
}
}
setOf는 toSet()을 통해 Set을 생성하고, toSet메소드는 LinkedHashSet을 이용하여 Set을 생성한다.
setOf를 통해 생성되는 Set은 LinkedHashSet이라는 것을 알 수 있다.
mutableSetOf 메소드를 살펴보면,
public fun <T> mutableSetOf(vararg elements: T): MutableSet<T> =
elements.toCollection(LinkedHashSet(mapCapacity(elements.size)))
mutableSetOf 또한 LinkedHashSet을 이용해 Set을 생성하는 것을 확인할 수 있다.
마지막으로 HashSet
val hashSet : HashSet<Int> = hashSetOf(1,2,3,4,5)
public fun <T> hashSetOf(vararg elements: T): HashSet<T> =
elements.toCollection(HashSet(mapCapacity(elements.size)))
hashSetOf() 메소드를 통해 Set을 생성하면 HashSet을 사용하면 된다.
HashSet이 가장 성능면에서는 좋을 것이다.
MutableSet 생성
자료구조가 LinkedHashSet인 MutableSet 생성
가장 대표적인 방법은 두가지가 있다.
- mutableSetOf() 메소드 사용
val mutableSet : MutableSet<Int> = mutableSetOf(1,2,3)
- Array를 toMutableSet() 메소드를 이용해 MutableSet으로 변환
intArrayOf(1,2,3,4,5).toMutableSet()
Array(5) { it }.toMutableSet()
자료구조가 HashSet인 MutableSet 생성
HashSet을 만들기 위해서는 직접 HashSet임을 지정해주어야 한다.
- hashSetOf() 메소드를 이용해 생성
val hashSet : HashSet<Int> = hashSetOf(1,2,3,4,5)
- Array를 toHashSet() 메소드를 이용해 MutableSet으로 변환
val intSet : MutableSet<Int> = intArrayOf(1,2,3,5,6).toHashSet()
val hashSet : MutableSet<Int> = Array(5) { it }.toHashSet()
값 접근 및 변경
값 접근
Set은 immutable 한 interface이므로, 값에 대한 접근만 가능하다. 따라서 여기서는 HashSet의 값을 접근하고 변경하는 방법을 설명한다.
val hashSet : HashSet<Int> = hashSetOf(1,2,3,4,5)
hashSet.forEach {
print(it) // 12345
}
for(i in hashSet) {
print(i) // 12345
}
값 변경하기
값 추가
- 특정 element 추가하기 : 중복되는 값이 있는지 확인 후 값이 없는 경우에만 추가
val hashSet : HashSet<Int> = hashSetOf(1,2,3,4,5)
hashSet.add(3)
hashSet.add(6)
hashSet.forEach {
print(it) // 123456
}
- Set 간에 plus 연산
val hashSet1 : HashSet<Int> = hashSetOf(1,2)
val hashSet2 : HashSet<Int> = hashSetOf(3,4,5)
hashSet2 += hashSet1;
hashSet2.forEach {
print(it) // 12345
}
값 제거
- 특정 element 제거
val hashSet : HashSet<Int> = hashSetOf(1,2,3)
hashSet.remove(2)
hashSet.forEach {
print(it) // 13
}
- Set간에 뺄셈 연산 : Set 사이에서 operator function인 minus(-) 연산으로 제거가 가능
val hashSet1 : HashSet<Int> = hashSetOf(1,2,3,4)
val hashSet2 : HashSet<Int> = hashSetOf(3,4,5,6)
hashSet2 -= hashSet1;
hashSet2.forEach {
print(it) // 56
}
교집합
- 집합이므로 교집합(intersect) 또한 가능하다
val hashSet1 : HashSet<Int> = hashSetOf(1,2,3,4)
val hashSet2 : HashSet<Int> = hashSetOf(3,4,5,6)
val intersectSet = hashSet1.intersect(hashSet2)
intersectSet.forEach {
print(it) // 34
}