HashMap은 키-값 쌍을 저장하는 데 사용할 수 있는 컨테이너예요. 다른 프로그래밍 언어에서는 이 자료 구조를 연관 배열이나 해시 테이블이라고 부르기도 해요. 맵에는 키의 집합이 들어 있고, 각 키는 특정 값에 대응해요. 저장된 값은 알맞은 키를 넘겨서 꺼낼 수 있어요. 키와 그에 딸린 값을 넣거나 삭제할 수도 있어요.
HashMap은 (표준 라이브러리의 대부분의 컬렉션처럼) 제네릭 컬렉션이라서, 사용자가 정의한 구조체와 튜플을 비롯해 다양한 타입을 키로 쓸 수 있어요. 값은 어떤 타입이든 될 수 있어요.
HashMap 만들기HashMap은 HashMap::new() 함수로 만들어요. 아래 코드는 팀 이름에 점수를 대응시킨 HashMap을 만들어요.
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
HashMap의 값에 접근하기HashMap 안에 값이 있다는 걸 알고 있다면 인덱싱 연산자([])를 쓰는 게 적절해요. 예를 들어 파란 팀의 점수를 꺼내려면 scores["Blue"]를 쓰면 돼요. 하지만 알맞은 키-값 쌍이 해시맵 안에 없으면 이 연산은 패닉을 일으켜요.
인덱싱 연산자 외에도 HashMap에 저장된 값에 접근하는 방법이 두 가지 더 있어요. 첫 번째는 get 멤버 함수를 쓰는 거예요.
if let Some(blue_score) = scores.get("Blue") {
println!("Blue scored: {blue_score} \n");
}
get은 요청한 키에 저장된 값에 접근해요. 넘긴 키가 HashMap에 없으면 None을, 키가 있으면 Some(value)를 반환해요. Rust의 Option에 대해 더 알고 싶다면 Option 개념을 확인해 봐요.
HashMap에 저장된 값에 접근하는 다른 방법은 entry 메서드를 쓰는 거예요. entry 메서드(또는 entry API)는 HashMap에서 키-값 쌍의 엔트리에 대한 참조를 반환해요. 엔트리는 해시에서 이 키의 상태를 나타내요. 키가 없으면 엔트리에 값이 없고, 값을 넣을 수 있게 해줘요.
let mut vote_counter: HashMap<_, usize> = HashMap::new();
let votes = ["Blue", "Red", "Red", "Blue", "Red", "Blue", "Blue"];
for vote in votes {
let count = vote_counter.entry(vote).or_default();
*count += 1;
}
println!("{vote_counter:#?}");
이 API는 몇 가지 흔한 접근 패턴을 아주 편리하게 만들어 줘요. 이를 위한 개념(Entry API)이 따로 있을 정도예요.
HashMap은 비교적 빠르고, 키 하나를 다루는 모든 연산에서 분할 상환 상수 시간 복잡도(O(1))를 가져요.
HashMap은 제네릭 자료 구조라서 임의의 타입을 키와 값으로 쓸 수 있는데, 제약이 하나뿐이에요. 어떤 타입을 키 타입으로 쓰려면 Eq와 Hash 두 트레이트를 구현해야 해요. 값 타입에는 트레이트 바운드가 없어요.