Ruby에서 값을 해시 테이블에 저장하는 과정은 매우 체계적입니다. 예를 들어 my_hash[:key] = "value"
와 같은 코드를 실행하면, Ruby는 먼저 :key
심볼을 내부 해시 함수에 전달하여 의사 난수 정수를 얻습니다. 이 해시 값을 빈의 총 개수(예: 64개)로 나눈 나머지를 계산하여 해당 값이 저장될 빈의 인덱스를 결정합니다. 예를 들어, :key
의 해시 값이 64로 나누어 2의 나머지를 반환하면, Ruby는 세 번째 빈(인덱스 2)에 해당 항목의 인덱스(예: 0)를 저장합니다. 실제 키와 값은 st_table_entry
라는 구조체로 entries
배열에 추가된 순서대로 저장됩니다. 이 방식은 나중에 값을 동일한 순서로 반환하는 데 용이합니다. 두 번째 요소인 my_hash[:key2] = "value2"
를 추가하는 경우에도 동일한 원리가 적용되어, entries
배열에 새로운 st_table_entry
가 채워지고 해당 빈에 인덱스가 저장됩니다.
해시 테이블의 진정한 이점은 값을 검색할 때 명확해집니다. p my_hash[:key]
와 같이 특정 키에 대한 값을 요청할 때, Ruby는 다시 해당 키의 해시 값을 계산하고 이를 빈의 개수로 나누어 나머지를 얻습니다. 이 나머지를 통해 Ruby는 필요한 키를 찾기 위해 해당 빈으로 즉시 이동할 수 있습니다. 예를 들어 :key
의 해시 값 계산 결과가 64로 나누어 2의 나머지를 가지면, Ruby는 빈 인덱스 2에서 항목 인덱스 0을 찾아 :key
에 해당하는 값을 즉시 검색합니다. 이는 배열이나 연결 리스트처럼 모든 요소를 순회하며 검색하는 방식에 비해 월등히 빠르고 효율적입니다.
Ruby의 해시 테이블 구현은 역사적으로 Peter Moore가 1980년대에 작성한 C 라이브러리를 기반으로 합니다. 2015년 Vladimir Makarov는 이 해시 테이블 코드를 재작성하여 st.c
및 include/ruby/st.h
파일에서 엔트리 및 빈 배열을 연속적인 메모리 세그먼트에 저장하도록 변경했습니다. 이러한 최적화는 현대 CPU가 해시 테이블의 모든 데이터를 캐시하는 효율성을 높여 전체 프로세스 속도를 향상시키는 데 크게 기여했습니다. 모든 Ruby Hash 객체를 나타내는 RHash
구조체는 include/ruby/ruby.h
파일에 정의되어 있으며, 이 파일에는 RString
, RArray
, RValue
등 Ruby 소스 코드에 사용되는 다른 주요 객체 구조체들도 포함되어 있습니다. 또한, 8개 이하의 엔트리를 가진 작은 해시를 위한 별도의 최적화된 처리 방식도 존재합니다. 이와 더불어, 해시 충돌 발생 시 오픈 어드레싱 방식을 통해 다른 빈을 찾아 값을 저장함으로써 효율적인 데이터 관리가 이루어집니다.