반응형

✅ 해시테이블 (Hash Table)

📌 개념

  • 키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.
  • 내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용해 충돌을 해결함.

📌 동작 방식

  1. Key → 해시 함수 → 인덱스 계산
  2. 계산된 인덱스에 값을 저장
  3. 같은 인덱스에 이미 값이 있으면 충돌(Collision) 발생
  4. 충돌 해결법: 체이닝(Chaining), 오픈 어드레싱(Open Addressing)

📌 시간 복잡도 (평균적 상황 기준)

  • 삽입: O(1)
  • 삭제: O(1)
  • 탐색: O(1)

※ 최악의 경우에는 O(n) (모든 키가 같은 인덱스로 해시됨)


✅ 딕셔너리 (Dictionary in C#)

📌 개념

  • C#에서의 Dictionary<TKey, TValue>는 해시테이블 기반으로 구현된 제네릭 키-값(key-value) 쌍 자료구조.
  • 즉, 해시테이블을 제네릭 버전으로 구현한 클래스라고 보면 됨.

📌 사용 예시

Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 25;
ages["Bob"] = 30;

Console.WriteLine(ages["Alice"]);  // 25

📌 주요 메서드

  • Add(key, value) : 새 항목 추가
  • Remove(key) : 항목 제거
  • ContainsKey(key) : 특정 키 존재 여부 확인
  • TryGetValue(key, out value) : 키가 있으면 값을 out 파라미터로 반환

🔍 해시테이블 vs 딕셔너리 차이점

제네릭 지원 ❌ (object 기반)
타입 안정성 낮음 (박싱/언박싱 필요) 높음
성능 상대적으로 느릴 수 있음 더 빠름
쓰레드 안전 기본적으로 안전하지 않음 마찬가지 (별도 처리 필요)
사용 권장 구형 코드 유지 보수시 신규 개발시 권장

 

사실 해시테이블의 상위호환이 딕셔너리 라고 한다. 해시테이블은 사용할떄마다 박싱/언박싱을 하기 때문에 너무 비효율 적이다. 쓰레드까지 들어간다면 솔직히 잘 모르겠다.. 어떻게 안전하지 않다는 기준은 뭔진 모르겠다. 

그리고 제네릭 지원이라는 것이 궁금했다. 제너릭이란건 자세히 뭘까

 

✅ 제네릭이란?

  • 하나의 클래스나 메서드가 다양한 데이터 타입을 처리할 수 있도록 해주는 기능
  • 코드를 타입에 의존하지 않고 작성하고, 나중에 사용할 때 구체적인 타입을 지정
  • 성능이 좋고 타입 안정성도 보장됨 (컴파일 타임에 타입 검사)

🔍 예시 (제네릭 없이 작성한 코드)

ArrayList list = new ArrayList();
list.Add(1);          // int
list.Add("hello");    // string

int num = (int)list[0];  // 형변환 필요 (박싱/언박싱)
  • 타입이 object이기 때문에 어떤 값이든 넣을 수 있지만,
  • 형변환이 필요하고, 런타임 오류가 발생할 수 있음

✅ 제네릭 사용 예시

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);

int num = intList[0]; // 형변환 필요 없음!
  • List<int>는 int형만 저장 가능
  • 컴파일 타임에 타입 검사 가능 → 오류 예방
  • 형변환이 없어져서 성능도 좋아짐

🔧 직접 만든 제네릭 클래스 예시

 
public class Box<T>  // T는 타입 매개변수 (Type Parameter)
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

💡 왜 제네릭을 사용할까?

이유설명
코드 재사용성 하나의 코드로 다양한 타입 처리 가능
타입 안정성 컴파일 타임에 타입 검사 가능
성능 향상 박싱/언박싱 제거로 메모리/속도 효율적

흠..GPT가 BOX<T>라는 예시를 줬는데 이 <T>가 뭔지 정확히 모르겠다 잘 사용하지 않기도 했고. 그래서 물어봤다.

 

✅ Box<T>란?

📦 정의

  • Box<T>는 "어떤 타입 T의 데이터를 담을 수 있는 상자"를 의미하는 클래스야.
  • 여기서 T는 타입 매개변수(Type Parameter) 로, 사용할 때 구체적인 타입으로 대체돼.
public class Box<T>
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

✅ 핵심 개념

용어설명
T 타입 매개변수. 보통 T(Type), K(Key), V(Value) 같은 문자로 씀
Box<T> T라는 타입을 나중에 지정할 수 있는 클래스
재사용성 Box<int>, Box<string> 등으로 여러 타입에 재사용 가능
 

✅ 실제 사용 예

Box<int> intBox = new Box<int>();
intBox.SetItem(10);                  // int 타입 사용
Console.WriteLine(intBox.GetItem());  // 출력: 10

Box<string> strBox = new Box<string>();
strBox.SetItem("hello");            // string 타입 사용
Console.WriteLine(strBox.GetItem());  // 출력: hello
  • Box<int>라고 선언하면 T는 int로 바뀌고,
  • Box<string>이라고 하면 T는 string으로 바뀌는 거야.

✅ 왜 이렇게 쓰는 걸까?

  1. 타입마다 클래스를 일일이 만들 필요 없음
  2. 형변환 없이 안전하게 값 사용 가능
  3. 컴파일 시 타입 검사 가능 → 오류 예방

✅ Box<T>를 실생활에 비유하면?

  • Box<T>는 "어떤 물건이든 담을 수 있는 상자"
  • 사용할 때 "이 상자에는 **책(int)**만 담을 거야", 혹은 "**편지(string)**만 담을 거야" 하고 말해주는 느낌이야.

흠..일단 그럼 T는 다양한 형식을 받을 수 있지만 그 중에 1종류만 받는다는 느낌이 강하다 생각했다 그러면 <T>라면 정말 어떤 타입이 올지 몰라서 대기하는 느낌이고 평소에 게임을 만든다면 <int>등등 정확한 타입을 써서 정의내리는게 더 깔끔할듯하다. 

✅ 왜 Box<int> 같은 걸 쓸까?

1. 타입별 클래스를 중복 작성할 필요 없게 해줌

❌ 제네릭 없이 하면:

public class IntBox {
    public int item;
}

public class StringBox {
    public string item;
}
  • int, string, float… 다 다른 클래스를 만들어야 해.
  • 유지보수 힘듦 + 코드 중복

✅ 제네릭을 쓰면:

public class Box<T> {
    public T item;
}
  • 한 번만 만들면 어떤 타입이든 다 쓸 수 있어: Box<int>, Box<string>, Box<float>...

2. 타입 안정성과 성능을 동시에 보장

❌ object 기반이면:

 
public class ObjectBox {
    public object item;
}
ObjectBox box = new ObjectBox();
box.item = 10;

int value = (int)box.item; // 형변환 필요 → 실수로 string을 넣었다면? → 런타임 오류
  • object는 어떤 타입이든 담을 수 있지만 형변환 필요하고, 런타임 오류 가능성 있음

✅ 제네릭 쓰면:

Box<int> box = new Box<int>();
box.item = 10;

int value = box.item; // 형변환 없음! 타입 안전!
  • 컴파일 타임에 타입 검사
  • 형변환 없어져서 성능도 더 좋음

3. 자료구조를 만들 때 필수

예를 들어, 우리가 직접 Stack, Queue, List 같은 자료구조를 만든다고 해보자.

public class MyStack<T> {
    private List<T> data = new List<T>();

    public void Push(T item) {
        data.Add(item);
    }

    public T Pop() {
        T item = data[data.Count - 1];
        data.RemoveAt(data.Count - 1);
        return item;
    }
}

📌 결론

Box<int>는 그냥 "정수를 담는 상자"고, 제네릭을 사용함으로써 우리는

  • 중복 코드 없이
  • 타입 안정성 확보하면서
  • 다양한 타입에 대해 유연하게 대응 가능한 클래스를 만들 수 있어.

 

음.. 그렇다고한다 확실히 제네릭을 쓰는게 훨씬 효율적이고 형변환도 필요없으니 좋은듯하다.

반응형

'C#' 카테고리의 다른 글

C# - 자료구조  (0) 2025.07.02
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)  (1) 2025.07.02
C# - .NET Runtime  (0) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
C# - 박싱/언박싱과 박싱/언박싱&형변환 차이  (0) 2025.07.01