Java

[Java] Map<K, V> 컬렉션 인터페이스

SN.Flower 2023. 10. 12.


Map<K, V> 컬렉션의 특징

 

  • Key와 Value 한 쌍으로 데이터를 저장

Map<K, V> 컬렉션은 Key(키)와 Value(값)의 한 쌍으로 데이터를 저장한다. 이 때 한 쌍의 데이터를 '엔트리(entry)'라고 하며, Map.Entry 타입으로 정의된다.

즉, Map<K, V>는 데이터를 엔트리 단위로 입력받는 것이다.

 

  • Key는 중복 저장 불가, Value는 중복 가능

Map<K, V> 컬렉션은 데이터를 구분하는 기준이 Key 값이기 때문에 List<E>에서 index가 중복되지 않았던 것처럼 Key 값도 중복될 수 없다.

하지만 Value 값은 Key 값으로 구분해 가져올 수 있으므로 중복이 허용된다.

 

Map<K, V> 인터페이스의 주요 메서드

 

구분 리턴 타입 메서드명 기능
데이터 추가 V put(K key, V value) 입력매개변수의 (key, value)를 Map 객체에 추가
void putAll(Map<? extends K, ? extends V> m) 입력매개변수의 Map 객체를 통째로 추가
데이터 변경 V replace(K key, V value) Key에 해당하는 값을 Value 값으로 변경(old 값 리턴)
단, 해당 Key가 없으면 null 리턴
boolean replace(K key, V oldValue, V newValue) (key, oldValue)의 쌍을 갖는 엔트리에서 oldValue를 newValue로 변경
단, 해당 엔트리가 없으면 false를 리턴
데이터
정보 추출
V get(Object key) 매개변수의 Key 값에 해당하는 Value를 리턴
boolean containsKey(Object key) 매개변수의 Key 값이 포함돼 있는지 여부를 리턴
boolean containsValue(Object value) 매개변수의 Value 값이 포함돼 있는지 여부를 리턴
Set<K> keySet() Map 데이터들 중 Key들만 뽑아 Set 객체로 리턴
Set<Entry<K, V>> entrySet() Map의 각 엔트리들을 Set 객체로 담아 리턴
int size() Map에 포함된 엔트리의 개수를 리턴
데이터 삭제 V remove(Object key) 입력매개변수의 Key를 갖는 엔트리 삭제
단, 해당 Key가 없으면 아무런 동작을 하지 않음
boolean remove(Object key, Object value) 입력매개변수의 (key, value)를 갖는 엔트리 삭제
단, 해당 엔트리가 없으면 아무런 동작을 하지 않음
void clear() Map 객체 내의 모든 데이터 삭제

 

Map<K, V>는 인터페이스이기 때문에 객체를 생성하기 위해서는 하위 구현 클래스를 통해 객체를 생성해서 위 메서드를 구현해야 한다.

대표적인 구현 클래스로는 HashMap<K, V>, LinkedHashMap<K, V>, Hashtable<K, V>, TreeMap<K, V>가 있다.

 

HashMap<K, V>와 Hashtable<K, V>는 Key 값을 HashSet<E>로 관리하고, LinkedHashMap<K, V>와 TreeMap<K, V>는 Key 값을 각각 LinkedHashSet<E> 및 TreeSet<E>로 관리한다.

 

HashMap<K, V> 구현 클래스

 

HashMap<K, V>는 Map<K, V>의 대표적인 구현 클래스로, Key 값의 중복을 허용하지 않는다.

Key 값의 중복 여부를 확인하는 메커니즘은 HashSet<E>와 완벽히 동일하다. 즉, 두 Key 객체의 hashcode()값이 같고, equals() 메서드가 true를 리턴하면 같은 객체로 인식한다. 이외에는 서로 다른 객체로 간주해 각각의 Key 값으로 등록할 수 있다.

 

public class HashMapExample {
    public static void main(String[] args) {
        Map<Integer, String> hashMap = new HashMap<>();

        // 데이터 추가
        hashMap.put(2, "나");
        hashMap.put(1, "가");
        hashMap.put(3, "다");
        System.out.println(hashMap);
        hashMap.putAll(hashMap);
        System.out.println(hashMap);

        // 데이터 변경
        hashMap.replace(1, "가가가");
        hashMap.replace(2, "나", "나나나");
        hashMap.replace(3, "다다", "다다다");
        System.out.println(hashMap);
    }
}

코드 실행 결과

 

HashMap<K, V> 객체를 생성한 후 3쌍의 데이터를 추가했다.

결과를 보면 알 수 있겠지만, 데이터의 입력 순서와 출력 순서는 동일하지 않을 수 있다.

 

putAll() 메서드를 사용해서 자신을 추가했을 때도 모든 Key 값이 중복되므로 엔트리의 변화는 없다.

 

replace() 메서드의 매개변수로 Key 값과 새로운 Value 값만 전달할 때는 동일한 Key 값이 있다면 해당 엔트리의 Value를 새로운 Value 값으로 바꿀 수 있다.

하지만 Key 값과 기존 Value 값, 새로운 Value 값을 매개변수로 전달할 때는 Key값과 기존 Value값이 둘 다 동일해야 새로운 Value 값으로 바꿀 수 있다.

 

// 정보 추출
System.out.println(hashMap.get(1));
System.out.println(hashMap.containsKey(2));
System.out.println(hashMap.containsKey(10));
System.out.println(hashMap.containsValue("나나나"));
System.out.println(hashMap.containsValue("다다다"));

Set<Integer> keySet = hashMap.keySet();
System.out.println(keySet);
Set<Map.Entry<Integer, String>> entrySet = hashMap.entrySet();
System.out.println(entrySet);

System.out.println(hashMap.size());

코드 실행 결과

 

이어서 데이터 정보 추출에 대한 메서드들을 실행해봤다.

 

get() 메서드는 매개변수로 들어간 Key 값과 동일한 Key 값을 가진 엔트리의 Value 값을 리턴해준다.

 

containsKey() 메서드는 매개변수로 들어간 Key 값과 동일한 Key 값을 가진 엔트리가 있다면 true를, 없다면 false를 리턴한다.

containsValue() 메서드는 containsKey()의 Key 값이 Value 값으로만 바뀔 뿐 작동 방식은 동일하다.

 

keySet() 메서드는 현재 저장돼 있는 엔트리 중 Key 값만을 Set<E>로 가져온다.

entrySet() 메서드는 Map<K, V>에 포함된 모든 엔트리를 Set<E>로 가져온다.

 

size() 메서드는 Map<K, V>의 데이터 개수를 리턴한다.

 

// 데이터 삭제
hashMap.remove(1);
System.out.println(hashMap);

hashMap.remove(2, "나나나");
hashMap.remove(3, "다다다");
System.out.println(hashMap);
        
hashMap.clear();
System.out.println(hashMap);

코드 실행 결과

 

remove() 메서드의 매개변수로 Key 값만 전달한다면 동일한 Key 값을 가진 엔트리를 찾아 삭제한다.

그리고 Key값과 Value 값을 전달한다면 둘 다 동일한 엔트리를 찾아서 삭제하고, 둘 중 하나라도 동일하지 않다면 삭제하지 않는다.

 

clear() 메서드는 모든 데이터를 삭제한다.

 

Hashtable<K , V> 구현 클래스

 

HashMap<K, V> 구현 클래스는 단일 쓰레드에 적합한 반면, Hashtable<K, V>는 멀티 쓰레드에 안정성을 가진다.

모든 내부의 주요 메서드가 동기화 메서드로 구현돼 있으므로 멀티 쓰레드에서도 안전하게 동작한다.

 

LinkedHashMap<K, V> 구현 클래스

 

LinkedHashMap<K, V>는 HashMap<K, V>의 기본적인 특성에 입력 데이터의 순서 정보를 추가로 갖고 있는 컬렉션이다.

따라서 저장 데이터를 출력하면 항상 입력된 순서대로 출력된다.

HashMap<K, V>에서는 Key를 HashSet<E>로 관리하는 반면에, LinkedHashMap<K, V>는 Key를 LinkedHashSet<E>로 관리한다.

Key의 순서 정보를 갖고 있으므로 Key 값을 기반으로 출력되는 LinkedHashMap<K, V> 또한 순서 정보를 갖게 되는 것이다.

 

TreeMap<K ,V> 구현 클래스

 

 TreeMap<K, V>는 Map<K, V>의 기본 기능에 정렬, 검색 기능이 추가된 컬렉션으로, 입력 순서와 관계없이 데이터를 Key 값의 크기 순으로 저장한다.

따라서 반드시 Key 객체는 크기 비교의 기준을 갖고 있어야 한다.

크기 비교 방법과 객체에 크기 비교 기준을 포함하는 모든 과정은 이전 글에서 설명한 TreeSet<E>과 동일하다.

 

TreeSet<E>의 상속 구조와 비슷하게 TreeMap<K, V>도 정렬, 검색 기능이 있는 메서드를 가진 SortedMap<K ,V> 와 NavigableMap<K, V> 인터페이스의 자식 클래스다.

따라서 TreeMap<K, V> 생성자로 객체를 생성해도 Map<K, V> 타입으로 선언하면 추가된 정렬 및 검색 기능을 사용할 수 없다. 즉, TreeMap<K, V>로 선언해야 추가된 정렬 및 검색 메서드를 호출할 수 있다.

 

Map<Integer, String> treeMap1 = new TreeMap<>(); 		// 추가된 정렬, 검색 메서드 사용 불가
TreeMap<Integer, String> treeMap2 = new TreeMap<>(); 	// 추가된 정렬, 검색 메서드 사용 가능

 


Reference

  • 자바 완전 정복 | 김동형 지음

댓글