개요

C#에서 키와 값으로 데이터를 저장하는 대표 컬렉션은 Hashtable과 Dictionary 두 가지가 있음 표면적인 사용법은 비슷하지만 제네릭 지원 여부와 타입 처리 방식이 달라 성능과 안정성에서 차이 발생 핵심 차이와 간단 예시, 선택 기준 정리

핵심 차이

  • Hashtable

    • 비제네릭 컬렉션, Key와 Value가 object로 저장됨
    • 값 형식 저장 시 박싱 발생, 꺼낼 때 언박싱 또는 캐스팅 비용 발생
    • 컴파일 타임 타입 체크 부재, 런타임 캐스팅 오류 위험 높음
    • 레거시 코드와의 호환성은 높으나 일반적으로 권장되지 않음
  • Dictionary<TKey, TValue>

    • 제네릭 컬렉션, 선언 시 Key와 Value의 타입 고정
    • 박싱/언박싱 없음(값 형식 포함), 타입 안정성 확보
    • 대체로 더 빠르고 풍부한 API 제공

참고로 언박싱은 값 형식에만 해당되는 개념이며, 참조 형식에 대한 as 또는 캐스트는 언박싱이 아님

사용 예시

  • Hashtable 최소 예시
var table = new Hashtable()
table["k1"] = 1             // 값 형식 → 박싱 발생
int v = (int)table["k1"]     // 언박싱 필요
  • Dictionary 최소 예시
var dict = new Dictionary<string, ExampleClass>()
dict.Add("data1", new ExampleClass { Name = "item1", Count = 1 })
if (dict.TryGetValue("data1", out var item)) Console.WriteLine(item.Name)

선택 기준과 주의

  • 언제 Dictionary 선택

    • 고정된 타입을 저장할 때 기본 선택
    • 값 형식을 자주 저장하거나 성능 민감할 때 유리
    • API 사용성, 가독성, 컴파일 타임 타입 체크 확보
  • 언제 Hashtable 선택

    • 레거시 코드나 비제네릭 API와의 호환이 필요한 경우에 한정
    • 다양한 타입을 섞어 저장해야 한다면 대안으로 Dictionary<string, object> 고려 가능
  • 공통 주의사항

    • 키는 null 불가, 값은 null 허용 여부는 타입에 따라 다름
    • 키 타입은 Equals와 GetHashCode의 일관성 필수, 그렇지 않으면 조회 실패나 중복 키 문제 발생
    • 멀티스레드 환경에서 동시 수정 필요 시 외부 동기화 또는 ConcurrentDictionary 사용 권장
    • ContainsKey 후 인덱서 접근 대신 TryGetValue 사용으로 이중 조회 방지
    • 열거 순서에 의존한 로직 지양, 구현에 따라 변경 가능성 존재

마무리

사용법은 비슷하지만 타입 안정성과 성능 측면에서 Dictionary가 일반적인 기본값 Hashtable은 특별한 호환성 요구가 있을 때만 선택 값 형식 저장, 타입 안정성, 성능 요구가 있다면 Dictionary를 우선 고려 다양한 타입 혼재가 필요하면 Dictionary<string, object>나 구분 가능한 래퍼 타입으로 모델링 권장

참고자료