백엔드/JAVA

[Java] 맵(Map)

mike705114 2024. 12. 4. 19:56

Map은 Java의 컬렉션 프레임워크에서 키-값(key-value) 쌍으로 데이터를 저장하고 관리하는 인터페이스입니다. 데이터 조회, 삽입, 삭제가 효율적이며, List와 달리 순서를 보장하지 않는 것이 특징입니다. Map의 주요 구현체로는 HashMap, LinkedHashMap, TreeMap, Hashtable 등이 있습니다.

 


 

1. Map의 특징

  1. Key-Value 구조:
    • key는 고유하며, 중복을 허용하지 않습니다.
    • value는 중복이 가능합니다.
  2. null 허용 여부:
    • 대부분의 구현체(HashMap 등)는 null 키와 다수의 null 값을 허용합니다.
    • 단, TreeMap과 같은 일부 구현체는 null 키를 허용하지 않습니다.
  3. 순서 보장 여부:
    • HashMap: 삽입 순서를 보장하지 않음.
    • LinkedHashMap: 삽입 순서를 보장.
    • TreeMap: 키를 정렬된 순서로 저장.

 

 


 

2. 주요 메서드

 

메서드 설명
put(K key, V value) 키와 값을 추가하거나, 기존 키에 대해 값을 업데이트합니다.
get(Object key) 키에 해당하는 값을 반환합니다. 값이 없으면 null을 반환합니다.
remove(Object key) 특정 키-값 쌍을 제거합니다.
containsKey(Object key) 특정 키가 존재하는지 확인합니다.
containsValue(Object value) 특정 값이 존재하는지 확인합니다.
size() Map에 저장된 키-값 쌍의 개수를 반환합니다.
keySet() 모든 키를 Set으로 반환합니다.
values() 모든 값을 Collection으로 반환합니다.
entrySet() 모든 키-값 쌍을 Set<Map.Entry<K, V>>로 반환합니다.
clear() 모든 데이터를 삭제합니다.
isEmpty() Map이 비어 있는지 확인합니다.

 


 

 

3. Map의 주요 구현체 비교

구현체 순서보장 정렬기준 동기화 여부 특이점
HashMap 보장하지 않음 없음 비동기 가장 일반적으로 사용, 빠른 성능.
LinkedHashMap 삽입 순서를 유지 없음 비동기 순서를 유지하면서도 성능이 우수.
TreeMap 키의 정렬된 순서 유지 키의 자연 순서 또는
Comparator
비동기 NavigableMap 인터페이스를 구현.
Hashtable 보장하지 않음 없음 동기화 레거시 클래스, 동기화를 제공.
ConcurrentHashMap 보장하지 않음 없음 동기화 (고성능) 병렬 처리를 위해 최적화된 Map.

 

4. 예제 코드

(1) 기본적인 HashMap 사용

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // 데이터 추가
        map.put("Alice", 25);
        map.put("Bob", 30);
        map.put("Charlie", 35);

        // 데이터 조회
        System.out.println("Bob's Age: " + map.get("Bob"));

        // 데이터 삭제
        map.remove("Alice");

        // 모든 키 출력
        for (String key : map.keySet()) {
            System.out.println("Key: " + key);
        }

        // 모든 값 출력
        for (Integer value : map.values()) {
            System.out.println("Value: " + value);
        }
    }
}

 

(2) 정렬된 키를 유지하는 TreeMap 사용

import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<String, Integer> treeMap = new TreeMap<>();

        treeMap.put("Banana", 20);
        treeMap.put("Apple", 30);
        treeMap.put("Cherry", 10);

        // 키의 자연 순서로 출력
        for (String key : treeMap.keySet()) {
            System.out.println(key + ": " + treeMap.get(key));
        }
    }
}

 

(3) 동기화가 필요한 경우 ConcurrentHashMap

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();

        concurrentMap.put("Thread1", 1);
        concurrentMap.put("Thread2", 2);

        concurrentMap.forEach((key, value) -> 
            System.out.println(key + ": " + value));
    }
}

 


 

5. Map 사용 시 유용한 팁

  1. 기본적으로 HashMap 사용:
    • 속도와 메모리 효율이 우수.
    • 순서를 보장해야 한다면 LinkedHashMap 사용.
  2. 동기화 필요 시:
    • 동기화가 필요한 경우 ConcurrentHashMap 사용.
    • Collections.synchronizedMap()으로 간단히 동기화 가능.
  3. 정렬된 데이터가 필요하다면 TreeMap:
    • 정렬이 필요하다면 TreeMap을 고려하되 성능 저하를 주의.
  4. 람다식과 Stream 활용:
    • entrySet(), keySet(), values()와 Stream API를 활용해 간결하고 가독성 있는 코드를 작성.

 


 

6. Map이 자주 쓰이는 경우

1. 빈도수 계산

  • 문제 유형: 문자열이나 배열에서 특정 요소의 등장 횟수를 계산해야 할 때.
  • 예제:
    • 문자열에서 각 문자의 빈도를 계산 (문자열 압축, 애너그램 확인).
    • 배열에서 등장 횟수가 가장 많은 요소 찾기.
import java.util.HashMap;

public class FrequencyCounter {
    public static void main(String[] args) {
        String input = "aabbbc";
        HashMap<Character, Integer> frequencyMap = new HashMap<>();

        for (char c : input.toCharArray()) {
            frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1);
        }

        System.out.println(frequencyMap); // {a=2, b=3, c=1}
    }
}

 


2. 애너그램 확인

  • 문제 유형: 두 문자열이 같은 문자의 조합인지 확인.
  • 설명: 각 문자의 빈도를 Map에 저장하고 비교.
import java.util.HashMap;

public class AnagramCheck {
    public static boolean isAnagram(String s1, String s2) {
        if (s1.length() != s2.length()) return false;

        HashMap<Character, Integer> charCount = new HashMap<>();
        for (char c : s1.toCharArray()) {
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
        }

        for (char c : s2.toCharArray()) {
            if (!charCount.containsKey(c) || charCount.get(c) == 0) {
                return false;
            }
            charCount.put(c, charCount.get(c) - 1);
        }

        return true;
    }

    public static void main(String[] args) {
        System.out.println(isAnagram("listen", "silent")); // true
        System.out.println(isAnagram("hello", "world"));   // false
    }
}

 


3. 중복 요소 확인

  • 문제 유형: 배열이나 문자열에 중복된 요소가 있는지 확인.
  • 설명: Map으로 각 요소를 저장하면서 중복 여부를 체크.
import java.util.HashMap;

public class DuplicateChecker {
    public static boolean hasDuplicate(int[] nums) {
        HashMap<Integer, Boolean> map = new HashMap<>();
        for (int num : nums) {
            if (map.containsKey(num)) {
                return true;
            }
            map.put(num, true);
        }
        return false;
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 2};
        System.out.println(hasDuplicate(nums)); // true
    }
}

 


 

맵이 필요한 경우 정리

  1. 빠른 조회가 필요:
    • 특정 값을 빠르게 찾거나 확인해야 할 때.
    • 예: 빈도수 계산, 두 수의 합 문제.
  2. 키-값 관계로 데이터를 저장:
    • 그룹화, 매핑 작업이 필요할 때.
    • 예: 데이터를 특정 조건에 따라 그룹화.
  3. 효율적인 중복 체크:
    • 중복된 요소를 효율적으로 찾고 관리해야 할 때.
  4. 누적 합 문제:
    • 부분 배열 합 문제처럼 중간 결과를 저장하고 활용할 때.

Map은 O(1)에 가까운 조회 성능을 제공하므로, 코딩 테스트에서 시간을 절약하고 효율적으로 문제를 해결하는 데 매우 유용합니다!