본문 바로가기
Java

[Java] 자료구조 Map 에 관하여

by jn4624 2022. 7. 19.
반응형

1. Map과 HashMap의 차이

둘의 가장 큰 차이는 특정 키에 대한 값을 찾는 과정에서

HashMap은 이름 그대로 HashTable을 이용하여 키-값 관계를 유지하며,

Map은 red-black tree 알고리즘을 이용한다.

 

일반적으로 Java Code에서 HashMap을 선언하여 사용한다고 할 때 아래와 같이 선언하여 사용할 수 있으며,

그 둘은 동일하게 HashMap을 사용할 수 있도록 선언한 코드에 해당한다.

HashMap<String, Object> map1 = new HashMap<>();

Map<String, Object> map2 = new HashMap<>();

HashMap으로 받을 때와 Map으로 받을 때의 차이를 알아보자.

 

위 코드에서 두번째 라인 Map은 "HashMap이 구현하는 인터페이스" 개념으로 이해하면 된다.

그렇기 때문에 실질적으로 위 두 코드 모두 정상적으로 HashMap을 선언하여 사용할 수 있고 그 기능상에서 차이를 보이지 않는다.

 

하지만 위 두 코드는 코드의 유지보수성을 고려하였을 경우 차이를 보인다.

 

Java에서는 HashMap 이외에도 다양한 종류의 Map이 존재한다.

그리고 이 다양한 종류의 Map은 모두 Map 인터페이스를 구현하는 구조로 정의되어 있다.

그렇기 때문에 그냥 Map 인터페이스를 사용하여 두번째 라인 코드처럼 HashMap을 사용하게 되면 추후 HashMap이 아닌 다른 종류의 Map을 사용해야 하는 상황이 발생하더라도 많은 코드 수정 없이 손 쉽게 이를 반영할 수 있다.

 

하지만 첫번째 라인 코드처럼 작성하게 된다면 해당 Map Object는 오직 HashMap에 대한 Object만을 담을 수 있기 때문에

앞에서 살펴본 두번째 라인 코드에 비해 다소 많은 코드 수정과 불편함을 겪게 된다.

즉, 유지보수성이 더 떨어진다고 할 수 있다.

 

2. Map

Map은 키(Key)와 값(Value)을 가진 데이터를 저장하는데 사용되는 인터페이스다.

Map 인터페이스만으로는 데이터를 보유할 수 없지만, 해당 구현 클래스의 객체를 생성한 후 Map 참조를 이용하여 객체를 보유할 수 있다.

Map의 구현체로는 HashMap, TreeMap, HashTable, LinkedHashMap이 있다.

순서는 유지되지 않으며 키의 중복을 허용하지 않고 값의 중복은 허용한다.

 

3. HashMap

Map의 특징인 키와 값의 쌍으로 이루어지며 키 또는 값으로써 null을 허용한다.

키의 중복을 허용하지 않고 값의 중복은 허용하며 동기화를 지원하지 않아 단일스레드 환경에서 사용하기 좋은 자료구조이다.

내부 해시값에 따라 키의 순서가 정해지므로 순서는 유지되지 않는다.

 

HashMap은 내부적으로 Entry 배열을 만들어 관리한다.

map.put() 메소드를 이용해 Entry를 추가하면 키(Key)값으로 넘겨준 객체의 해시코드를 계산하여 Entry 배열의 접근 인덱스로 사용한다.

예를 들어 "key1" 이라는 문자열의 해시코드를 계산했을 때 10이라는 값이 나오면 배열의 10번째 항목으로 값(Value) 저장하는 식이다.

 

HashMap의 해시값 계산은 자바 버전에 따라 다르겠지만 기본적으로 객체의 hashCode() 메소드의 리턴 값을 기반으로 동작한다.

또한 해시충돌(Hash Collision)의 경우를 대비해 equals() 메소드까지 사용해 키값이 정말 일치하는 경우에만 값을 리턴한다.

따라서 키값으로 사용할 클래스의 특성에 따라 필요한 경우 hashCode() 메소드와 equals() 메소드를 오버라이드해야 할 수도 있다.

 

4. TreeMap

TreeMap 역시 중복을 허용하지 않으며 키와 값의 쌍으로 이루어져 있다.

HashMap과 다른 점은 SortedMap을 implements 하여 키값을 기준으로 정렬된 상태를 유지할 수 있다.

내부적으로 red-black tree로 저장하여 관리되며 키값에 대한 Compartor 구현으로 정렬 순서를 바꿀 수 있다.

 

TreeMap은 데이터를 내부적으로 RB-Tree 형태로 관리하기 때문에 데이터를 탐색하는데 O(log N)의 시간이 걸린다.

또한, 엘리먼트의 추가/삭제 역시 O(log N)의 시간이 걸린다.

따라서 HashMap과 비교해보면 좀 더 느린 속도를 갖는다.

 

5. HashMap과 TreeMap의 차이

HashMap은 어떤 순서도 유지하지 않지만 TreeMap은 요소의 자연 순서부에 따라 Sort된다.

내부적으로 HashMap 구현은 Hashing을 사용하고 TreeMap은 red-black tree 구현을 사용한다.

HashMap은 하나의 null 키와 많은 null 값을 저장할 수 있고 TreeMap은 null 키를 포함할 수 없지만 많은 null 값을 포함할 수 있다.

HashMap은 O(1)과 같은 get, put 메소드에 대해 일정한 시간 성능을을 나타내는 반면 TreeMap은 get, put 메소드에 대한 log(N) 시간 보장 비용을 제공한다.

HashMap의 성능 시간은 대부분의 작업에서 TreeMap에 비해 일정하므로 TreeMap보다 훨씬 빠르다.

HashMap은 비교에서 equals() 메소드를 사용하지만 TreeMap은 ordering을 유지하기 위해 compareTo() 메소드를 사용한다.

HashMap은 Map 인터페이스의 구현체고 TreeMap은 NavigableMap 인터페이스의 구현체이다.

 

6. HashTable

HashTable 역시 중복을 허용하지 않으며 키와 값의 쌍으로 이루어져 있다.

HashMap과 다른 점은 키와 값으로써 null을 허용하지 않는다.

동기화를 지원하여 thread-safe하다.

따라서, 멀티스레드 환경에서 사용하지 좋은 자료구조이다.

다른 쓰레드가 block되고 unblock되는 대기 시간을 기다리기 때문에 HashMap에 비해 느리다.

 

7. LinkedHashMap

LinkedHashMap은 데이터의 입력 순서를 기억한다.

LinkedHashMap에 저장되는 각 항목은 Map.Entiry 클래스를 구현한 Node 클래스로 내부에 before, after 멤버를 갖는 연결리스트 구조를 가지고 있다.

HashMap 클래스를 확장한 클래스로 엘리먼트의 추가/제거는 O(1) 시간에 이루어지고,

모든 멤버들을 순회하는 연산도 LinkedList의 각 항목을 순회하는 동작이므로 O(1) 시간에 이루어진다.

 

8. 요약

  • HashMap은 순서를 보장하지 않는다.
  • TreeMap은 키값으로 사용된 클래스의 비교 연산을 활용하여 순서를 보장한다(이 때 키값으로 사용한 클래스가 Comparator 인터페이스를 구현하게 된다면 원하는 대로 순서를 조정할 수 있다).
  • LinkedHashMap은 입력된 순서를 보장한다.

 

각각의 특성이 모두 다르지만, TreeMap이나 LinkedHashMap을 사용해야 하는 특별한 이유가 없다면 HashMap을 사용하자.

HashMap의 시간복잡도는 해시값을 사용하기 때문에 O(1)이다.

반면 RB-Tree를 사용하는 TreeMap의 접근 연산은 O(log N)이다. 대신 정렬된 순서를 얻을 수 있다.

LinkedHashMap의 경우 O(1)의 시간 복잡도를 갖지만 불필요하게 Linked-list를 유지하는 비용이 생길 수 있다.

 

 

🙏 참조 ::

반응형