Map에 키 값 쌍 추가하기

put(key, value)로 맵에 키/값 쌍을 간단히 추가할 수 있습니다. 키가 맵에 이미 존재하지 않으면 키/값 쌍이 단순히 맵에 추가됩니다. 있는 경우 기존 값이 새 값으로 바뀝니다.

두 경우 모두, put() 메서드는 현재 키에 바인딩된 기존 값을 반환합니다. 즉, 새 키인 경우 put()를 호출하면 null이 반환됩니다.

Java SE 8에는 putIfAbsent() 메서드가 도입되었습니다. 이 메서드는 키가 아직 존재하지 않고 값이 null이 아닌 경우에만 키/값 쌍을 Map에 추가할 수 있습니다. 처음에는 약간 혼란스러워 보일 수 있지만, putIfAbsent()는 null 값을 제공된 새 값으로 대체합니다.

이 메서드는 Map에서 잘못된 null 값을 제거해야 할 때 매우 유용합니다. 예를 들어 다음 코드는 널 Integerint 값으로 자동 언박싱할 수 없기 때문에 NullPointerException이 발생하여 실패합니다.

Map<String, Integer> map = new HashMap<>();
 
map.put("one", 1);
map.put("two", null);
map.put("three", 3);
map.put("four", null);
map.put("five", 5);
 
for (int value : map.values()) {
    System.out.println("value = " + value);
}

이 코드를 자세히 살펴보면 map.values()Collection<Integer>임을 알 수 있습니다. 따라서 이 컬렉션을 반복하면 Integer의 인스턴스가 생성됩니다. valueint로 선언했기 때문에 컴파일러는 이 Integerint 값으로 자동 언박싱합니다. 이 메커니즘은 Integer의 인스턴스가 널인 경우 NullPointerException과 함께 실패합니다.

결함이 있는 널 값을 기본값인 -1로 대체하는 다음 코드를 사용하여 이 맵을 수정하면 더 이상 NullPointerException이 생성되지 않습니다.

for (String key : map.keySet()) {
    map.putIfAbsent(key, -1);
}

이전 코드를 실행하면 다음과 같이 출력됩니다. 보시다시피 이 Map에는 더 이상 null 값이 포함되지 않습니다:

value = -1
value = 1
value = -1
value = 3
value = 5

 

키에서 값 가져오기

get(key) 메서드를 호출하여 주어진 키에 바인딩된 값을 얻을 수 있습니다.

Java SE 8에서는 키와 키가 Map에 없는 경우 반환되는 기본값을 취하는 getOrDefault() 메서드가 도입되었습니다.

이 메서드가 실제로 어떻게 작동하는지 예시를 통해 살펴보겠습니다:

Map<Integer, String> map = new HashMap<>();
 
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
 
List<String> values = new ArrayList<>();
for (int key = 0; key < 5; key++) {
    values.add(map.getOrDefault(key,"UNDEFINED"));
}
 
System.out.println("values = " + values);

또는 스트림에 익숙한 경우(이 튜토리얼의 뒷부분에서 다룹니다):

List<String> values =
    IntStream.range(0, 5)
        .mapToObj(key -> map.getOrDefault(key, "UNDEFINED"))
        .collect(Collectors.toList());
 
System.out.println("values = " + values);

두 코드 모두 동일한 결과를 출력합니다:

values = [UNDEFINED, one, two, three, UNDEFINED]

 

Map에서 키 제거하기

키/값 쌍을 제거하려면 remove(key) 메서드를 호출하면 됩니다. 이 메서드는 해당 키에 바인딩된 값을 반환하므로 null을 반환할 수 있습니다.

해당 키에 바인딩된 값을 모르는 경우 키/값 쌍을 Map에서 무작위로 제거하는 것은 위험할 수 있습니다. 따라서 Java SE 8은 값을 두 번째 인수로 받는 오버로드를 추가했습니다. 이번에는 키/값 쌍이 Map의 키/값 쌍과 완전히 일치하는 경우에만 키/값 쌍이 제거됩니다.

remove(key, value) 메서드는 키/값 쌍이 Map에서 제거된 경우 부울 값인 true를 반환합니다.

 

키 또는 값의 존재 여부 확인하기

주어진 키 또는 주어진 값의 존재 여부를 확인하는 두 가지 메서드가 있습니다: containsKey(key)containsValue(value). 두 메서드 모두 Map에 지정된 키 또는 값이 포함되어 있으면 true를 반환합니다.

 

Map의 콘텐츠 확인하기

Map 인터페이스에는 Collection 인터페이스에 있는 것과 유사한 메서드도 제공됩니다. 이러한 메서드는 설명이 필요 없습니다: isEmpty()는 빈 Map의 경우 true를 반환하고, size()는 키/값 쌍의 수를 반환하며, clear()는 Map의 모든 콘텐츠를 제거합니다.

주어진 Map의 콘텐츠를 현재 Map에 추가하는 방법도 있습니다: putAll(otherMap). 두 Map 모두에 일부 키가 있는 경우 otherMap의 값은 이 Map의 값을 지웁니다.

 

Map의 키, 값 또는 항목에 대한 보기 가져오기

Map에 정의된 다른 Set을 가져올 수도 있습니다.

  • keySet(): map에 정의된 키를 포함하는 Set의 인스턴스를 반환합니다.
  • entrySet(): 맵에 포함된 키/값 쌍을 포함하는 Set<Map.Entry>의 인스턴스를 반환합니다.
  • values(): Map에 존재하는 값을 포함하는 Collection의 인스턴스를 반환합니다.

다음 예제는 이 세 가지 메서드가 실제로 작동하는 모습을 보여줍니다:

Map<Integer, String> map = new HashMap<>();
 
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.put(4, "four");
map.put(5, "five");
map.put(6, "six");
 
Set<Integer> keys = map.keySet();
System.out.println("keys = " + keys);
 
Collection<String> values = map.values();
System.out.println("values = " + values);
 
Set<Map.Entry<Integer, String>> entries = map.entrySet();
System.out.println("entries = " + entries);

이 코드를 실행하면 다음과 같은 결과가 생성됩니다:

keys = [1, 2, 3, 4, 5]
values = [one, two, three, four, five]
entries = [1=one, 2=two, 3=three, 4=four, 5=five]

이러한 Set은 현재 Map를 views 합니다. Map에 대한 모든 변경 사항은 해당 보기에 반영됩니다.

키 Set에서 키 제거하기

예를 들어, keySet() 호출로 반환된 set에서 키를 제거하면 해당 키/값 쌍이 맵에서 제거됩니다.

예를 들어 이전 Map에서 이 코드를 실행할 수 있습니다:

keys.remove(3);
entries.forEach(System.out::println);

It will produce the following resut:

1=one
2=two
4=four
5=five
6=six

값 Collection에서 값 제거하기

값이 Map에서 두 번 이상 발견될 수 있기 때문에 값을 제거하는 것은 간단하지 않습니다. 이 경우 값 컬렉션에서 값을 제거하면 가장 먼저 일치하는 키/값 쌍만 제거됩니다.

다음 예제에서 이를 확인할 수 있습니다.

Map<Integer, String> map =
    Map.ofEntries(
        Map.entry(1, "one"),
        Map.entry(2, "two"),
        Map.entry(3, "three"),
        Map.entry(4, "three")
    );
System.out.println("map before = " + map);
map = new HashMap<>(map);
map.values().remove("three");
System.out.println("map after = " + map);

이 코드를 실행하면 다음과 같은 결과가 생성됩니다.

map before = {1=one, 2=two, 3=three, 4=three}
map after  = {1=one, 2=two, 4=three}

보시다시피, 이 예제에서는 첫 번째 키/값 쌍만 제거되었습니다. 이 경우 주의해야 할 점은 선택한 구현이 HashMap인 경우 어떤 키/값 쌍이 발견될지 미리 알 수 없다는 것입니다.

하지만 이러한 Set에 대한 모든 작업에 액세스할 수 있는 것은 아닙니다. 예를 들어 키의 집합이나 값의 컬렉션에 요소를 추가할 수 없습니다. 그렇게 하려고 하면 UnsupportedOperationException이 발생합니다.

필요한 것이 Map의 키/값 쌍을 반복하는 것이라면 키/값 쌍의 집합을 직접 반복하는 것이 가장 좋습니다. 키 세트에 대해 반복하고 해당 값을 얻는 것보다 훨씬 더 효율적입니다. 사용할 수 있는 가장 좋은 패턴은 다음과 같습니다:

for (Map.Entry<Integer, String> entry : map.entrySet()) {
    System.out.println("entry = " + entry);
}