•
자바 8에서는 Map 인터페이스에 몇 가지 디폴트 메서드를 추가했다. 이는 기본적인 구현을 인터페이스에서 제공하는 기능 정도로 생각해두는 것이 편하다.
•
자주 사용되는 패턴을 개발자가 직접 구현할 필요가 없도록 구현을 추가한 것이다.
1. forEach 메서드
•
맵에서 키와 값을 반복하면서 확인하는 작업은 가장 잘 알려진 귀찮은 작업 중 하나다. Map.Entry<K, V>의 iterator를 사용하여 처리할 수 있다.
for (Iterator<Map.Entry<String, Integer>> iterator = humans.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + " " + entry.getValue());
}
Java
복사
•
자바 8에서부터 Map 인터페이스는 BiConsumer를 인수로 받는 forEach 메서드를 지원하므로 코드를 조금 더 간단하게 구현할 수 있다.
humans.forEach((friend, age) -> System.out.println(friend + " is " age + " years old");
Java
복사
•
forEach 디폴트 메서드의 구현은 다음과 같다.
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch (IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
Java
복사
2. 정렬 메서드
•
다음 두 개의 새로운 유틸리티를 이용하면 맵의 항목을 키 또는 값을 기준으로 정렬할 수 있다.
◦
Entry.comparingByKey
◦
Entry.comparingByValue
Map<String, Integer> humans = Map.ofEntries(Map.entry("A", 1), Map.entry("B", 2));
humans
.entrySet()
.stream()
.sorted(Entry.comparingByKey())
.forEachOrdered(System.out::println);
Java
복사
3. getOrDefault 메서드
•
기존에는 찾으려는 키의 값이 존재하지 않는 경우, Null이 반환되므로 NullPointerException을 방지하려면 결과가 Null인지 확인해야 했었다.
•
자바 8에서는 이를 getOrDefault 메서드를 통해 해결할 수 있다.
•
이 메서드는 첫 번째 인수로 키를, 두 번째 인수로 기본값을 받으며 맵에 키가 존재하지 않는다면 두 번째로 인수로 받은 기본값을 리턴한다.
•
만약 키가 존재하고 값이 Null이라면 getOrDefault는 Null을 반환한다. getOrDefault는 키가 존재하느냐의 여부에 따라서 두 번째 인수가 반환될지 말지를 결정한다는 것을 알아두자.
4. 계산 패턴
•
맵에 키가 존재하는지 여부에 따라 처리하고 결과를 저장해야 하는 상황이 필요한 경우가 있다.
•
예를 들어 키를 이용해 값비싼 동작을 실행해서 얻은 결과를 캐시하려 한다. 키가 존재하면 결과를 다시 계산할 필요가 없다. 다음의 세 가지 연산이 이런 상황에 도움을 줄 수 잇다.
◦
computeIfAbsent
▪
제공된 키에 해당하는 값이 없으면(값이 없거나 존재해도 Null인 경우) 키를 이용해 새 값을 계산하고 맵에 추가한다.
◦
computeIfPresent
▪
제공된 키가 존재한다면 새 값을 계산하고 맵에 추가한다.
◦
compute
▪
제공된 키로 새 값을 계산하고 맵에 저장한다.
5. 삭제 패턴
•
키를 인수로 받아 맵 항목을 제거하는 remove 메서드는 이미 알고 있다.
•
자바 8에서는 키가 특정한 값과 연관되었을 때만 항목을 제거하는 오버로드 버전 메서드를 제공한다.
humans.remove(key, value);
Java
복사
6. 교체 패턴
•
맵의 항목을 바꾸는 데 사용할 수 있는 두 개의 메서드가 추가되었다.
◦
replaceAll
▪
BiFunction을 적용한 결과로 각 항목의 값을 교체한다. 이 메서드는 앞서 살펴본 List의 replaceAll과 비슷한 동작을 수행한다.
◦
replace → 책에선 대문자로 되어있는데, 라이브러리 가보니까 소문자인거 같아서 소문자로 썼다.
▪
키가 존재하면 맵의 값을 바꾼다. 키가 특정 값으로 매핑되었을 때만 값을 교체하는 오버로드 버전도 있다.
Map<String, Integer> humans = new HashMap<>();
humans.put("A", 1);
humans.put("B", 2);
humans.replaceAll((name, age) -> name.toUpperCase());
Java
복사
•
불변 컬렉션이었다면 당연히 에러가 발생한다!
7. 합침
•
두 그룹의 연락처를 포함하는 두 개의 맵을 합친다고 가정하다. 다음처럼 putAll을 사용할 수 있다.
Map<String, Integer> firstHumans = Map.of("A", 1, "B", 1);
Map<String, Integer> secondHumans = Map.of("C", 1, "D", 1);
Map<String, Integer> allHumans = new HashMap<>();
allHumans.PutAll(firstHumans);
allHumans.PutAll(secondHumans);
Java
복사
◦
중복된 키가 존재하지 않는다면 위 연산을 잘 동작한다. 만약 중복된 키가 존재한다면?
•
merge 메서드를 이용해서 값을 좀 더 유연하게 병합할 수 있다.
Map<String, Integer> firstHumans = Map.of("A", 1, "B", 1);
Map<String, Integer> secondHumans = Map.of("B", 1, "D", 1);
Map<String, Integer> allHumans = new HashMap<>();
allHumans.PutAll(firstHumans);
allHumans.forEach((name, age) -> allHumans.merge(name, age, (age1, age2) -> age1 + age2)); -> // 중복값일 경우, 나이를 더하는 전략
Java
복사