🧐 Map Interface란?
Map 인터페이스는 키(key)와 값(value)을 하나의 쌍으로 묶어서 저장하는 컬렉션 클래스를 구현하는데 사용된다.
키는 중복될 수 없지만 값은 중복을 허용한다. 만약 중복된 키와 값을 저장한다면 기존의 값은 없어지고, 저장된 값이 남는다.
📝 Map의 대표적인 메서드
Map을 사용해서 출력할 때 보통 entrySet() 또는 keySet()메서드를 활용하여 Map의 객체를 반환받아 출력한다.
entrySet()은 key와 valeu 모두 필요할 경우 사용한다.
keySet()은 key 값만 필요할 경우 사용하는데 key값을 받고 get(key)를 활용하여 value도 출력할 수 있다.
이럴 경우 key를 찾고 value를 찾는 과정에서 시간이 많이 소요되므로 많은 양의 데이터를 가져온다면 entrySet()을 쓰자.
(약 20% ~ 200% 성능 차이가 있음)
https://web.archive.org/web/20140209172758/http://www.sergiy.ca/how-to-iterate-over-a-map-in-java/
위 사이트에서 Map을 반복할 때의 예제들이 나와있다.
1. For-Each 반복을 사용하여 키와 값이 모두 필요한 경우
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
entrySet()을 사용해서 키와 값을 가져왔다.
2. For-Each 반복을 사용하여 키 또는 값이 필요한 경우
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//iterating over keys only
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//iterating over values only
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
맵의 키 또는 값만 필요할 경우, entrySet() 대신에 keySet 또는 values()를 사용할 수 있다.
이 방법은 entrySet 반복에 비해 성능이 약간 우수하며 더 깔끔하다. (약 10% 더 빠르다)
3. Iterating using (반복중에 사용)
Map을 반복하는 도중에 Map의 데이터를 제거할 수 있는 유일한 방법이다. (iteraotr.remove())
4. 키를 반복하고 값을 검색하기 (비효율적)
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
1번과 비슷해보이지만 실제로는 시간이 많이 걸릴 수 있고 매우 느리고 비효율적이다.
(1번에 비해 20% ~ 200% 느림)
이 사이트에 Map을 반복하여 사용할 때의 예제들이 나와있었는데 결론은 키 또는 값만 필요한 경우 2번을 사용한다.
반복중에 삭제하려면 3번을 사용한다. 키와 값을 사용할 때는 1번을 사용한다.
📝 Map을 구현하는 클래스
1. HashMap
Map 컬렉션 클래스에서 가장 많이 사용되는 클래스이다.
Map의 특징처럼 키와 값을 묶어서 하나의 데이터(entry)로 저장한다.
해싱(hasing)을 사용하기에 많은 양의 데이터를 검색하는데 있어서 뛰어난 성능을 보인다.
(hash algorithm을 사용하여 검색 속도가 빠르다)
Entry[] table;
class Entry {
Object key;
Object value;
}
- HashMap은 키(key)와 값(value)에 null을 허용한다.
- 동기화를 보장하지 않는다.
thread-safe하지 않아서 싱글 쓰레드 환경에서 사용하는 것이 좋다.
이 HashMap은 검색 속도가 빠르다고 했는데 위에 나와있듯이 동기화 처리를 하지 않기 때문이다.
결국 HashTable과 ConcurrentHashMap보다 데이터 검색속도는 빠르지만 신뢰성과 안정성이 떨어진다.
2. Hashtable
HashMap 클래스와 같은 동작을 하는 클래스이다.
이 Hashtable과 HashMap의 관계는 Vector와 ArrayList 관계와 같다.
즉, 기존 코드와의 호환성 때문에 남아있는 것으로 Hashtable 클래스보다는 HashMap을 사용하는 것을 권장한다.
- 키와 값에 null을 허용하지 않는다.
- 동기화를 보장한다.
thread-safe하기 때문에 멀티 쓰레드 환경에서 사용할 수 있다.
이는 데이터를 다루는 메서드에 synchronized 키워드가 붙어 있다.
해당 키워드는 메서드를 호출하기 전에 쓰레드간 동기화 락을 건다.
그래서 멀티 쓰레드 환경에서도 데이터 무결성을 보장한다.
하지만 쓰레드간 동기화 락은 매우 느린 동작이다.
3. TreeMap
키와 값을 한 쌍으로 하는 데이터를 이진 검색 트리(Binary Search Tree)의 형태로 저장한다.
데이터를 추가하거나 제거하는 동작은 매우 빠르다.
NavigableMap 인터페이스를 기존의 이진 검색 트리의 성능을 향상시킨 레드-블랙 트리(Red-Black tree)로 구현했다.
데이터를 저장할 때 즉시 정렬하기에 추가나 삭제가 HashMap보다 오래 걸린다.
하지만 정렬된 상태로 Map을 유지하거나 정렬된 데이터를 조회해야 하는 범위 검색인 경우 성능이 빠르다.
4. ConcurrentHashMap
key와 value에 null을 허용하지 않는다.
동기화를 보장한다.
thread-safe하기 때문에 멀티 쓰레드 환경에서 사용할 수 있다.
이 구현체는 HashMap의 동기화 문제를 보완하기 위해 나온 것이다.
동기화 처리를 할 때 어떤 Entry를 조작하는 경우 해당 Entry만 락을 건다.
그래서 HashTable보다 데이터 다루는 속도가 빠르다.
즉, Entry 아이템별로 락을 걸어 멀티 쓰레드 환경에서의 성능을 향상시킨다.
싱글 쓰레드 환경이면 HashMap, 멀티 쓰레드 환경이면 ConcurrentHashMap을 쓰면 되겠다.
참고
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html
http://www.tcpschool.com/java/java_collectionFramework_map
https://tecoble.techcourse.co.kr/post/2021-11-26-hashmap-hashtable-concurrenthashmap/
'프로그래밍 > Java' 카테고리의 다른 글
[Java] sort와 parallelSort 비교 (0) | 2022.09.18 |
---|---|
Java - 값이 null,공백 등 Blank인지 확인하기 (0) | 2022.04.07 |
Set 인터페이스 (0) | 2022.02.24 |
Queue 인터페이스 (0) | 2022.02.22 |
List 인터페이스 (0) | 2022.02.22 |