해시셋
HashSet을 키에만 관심이 있는 HashMap이라고 생각해보세요 (HashSet<T>는 실제로 HashMap<T, ()>의 래퍼(wrapper)일 뿐입니다).
“그게 무슨 소용이죠?“라고 물으실 수 있습니다. “그냥 키를 Vec에 저장할 수도 있잖아요.”
HashSet의 고유한 특징은 중복된 요소가 없음을 보장한다는 것입니다. 이는 모든 집합(set) 컬렉션이 지키는 계약입니다. HashSet은 그 중 하나의 구현일 뿐입니다. (참고: BTreeSet)
이미 HashSet에 존재하는 값을 삽입하면 (즉, 새 값이 기존 값과 같고 해시도 같다면), 새 값이 기존 값을 대체하게 됩니다.
이는 무언가를 중복 없이 유지하고 싶을 때나, 이미 가지고 있는지 알고 싶을 때 매우 유용합니다.
하지만 집합은 그 이상의 일을 할 수 있습니다.
집합은 4가지 주요 연산을 가집니다 (다음 호출들은 모두 이터레이터를 반환합니다):
-
union(합집합): 두 집합에 있는 모든 고유한 요소들을 가져옵니다. -
difference(차집합): 첫 번째 집합에는 있지만 두 번째 집합에는 없는 모든 요소들을 가져옵니다. -
intersection(교집합): 오직 양쪽 집합 모두에 있는 모든 요소들을 가져옵니다. -
symmetric_difference(대칭차집합): 한쪽 집합에는 있지만 _양쪽 모두_에 있지는 않은 모든 요소들을 가져옵니다.
다음 예제에서 이들 모두를 시도해 보세요:
use std::collections::HashSet;
fn main() {
let mut a: HashSet<i32> = vec![1i32, 2, 3].into_iter().collect();
let mut b: HashSet<i32> = vec![2i32, 3, 4].into_iter().collect();
assert!(a.insert(4));
assert!(a.contains(&4));
// `HashSet::insert()`는 값이 이미 존재하면 false를 반환합니다.
assert!(b.insert(4), "값 4는 이미 집합 B에 있습니다!");
// FIXME ^ 이 줄을 주석 처리하세요
b.insert(5);
// 컬렉션의 요소 타입이 `Debug`를 구현한다면,
// 컬렉션도 `Debug`를 구현합니다.
// 보통 `[elem1, elem2, ...]` 형식으로 요소를 출력합니다.
println!("A: {:?}", a);
println!("B: {:?}", b);
// [1, 2, 3, 4, 5]를 임의의 순서로 출력합니다
println!("합집합: {:?}", a.union(&b).collect::<Vec<&i32>>());
// 이는 [1]을 출력해야 합니다
println!("차집합: {:?}", a.difference(&b).collect::<Vec<&i32>>());
// [2, 3, 4]를 임의의 순서로 출력합니다.
println!("교집합: {:?}", a.intersection(&b).collect::<Vec<&i32>>());
// [1, 5]를 출력합니다
println!("대칭차집합: {:?}",
a.symmetric_difference(&b).collect::<Vec<&i32>>());
}
(예제는 문서에서 발췌 및 수정되었습니다.)