빌드노트
HashMap의 getOrDefault가 기본값 대신 null을 반환한다
HashMap의 getOrDefault는 키가 없을 때만 기본값을 반환한다. 짧은 JShell 입력으로 키 부재와 null 매핑을 구분하고, 저장된 null도 기본값으로 바꿀지 결정한다.
HashMap에서 names.getOrDefault(key, "guest")를 호출했는데 null이 나오면, 해당 키에 null이 저장돼 있는지 확인한다. getOrDefault는 키가 없을 때만 기본값을 반환한다. 키가 있고 값이 null이면 그 값을 그대로 돌려준다. 저장된 null까지 "guest"로 바꾸려면 조회한 값의 null 여부를 따로 검사한다.
Java 17의 getOrDefault 문서 (새 탭에서 열림)가 정한 기준도 해당 키의 매핑이 있는지다. 매핑은 키와 값의 연결을 뜻한다. null을 값으로 저장해도 그 연결은 존재한다.
get이 같아도 키의 존재 여부는 다르다
다음 예제는 null 값을 허용하는 HashMap (새 탭에서 열림)을 사용한다. JDK 17이 설치되고 bin 폴더의 jshell 명령을 사용할 수 있으면 외부 라이브러리나 Java 파일 없이 실행할 수 있다. 터미널에서 jshell을 실행하고 아래 입력을 순서대로 넣는다. 줄 끝의 주석은 Windows OpenJDK 17.0.2의 JShell에서 실제로 확인한 반환값이다.
import java.util.HashMap;
var names = new HashMap<String, String>();
names.put("nullValue", null);
names.get("absent"); // null
names.get("nullValue"); // null
names.containsKey("absent"); // false
names.containsKey("nullValue"); // true
names.getOrDefault("absent", "guest"); // "guest"
names.getOrDefault("nullValue", "guest"); // null
absent는 저장하지 않았고, nullValue에는 Java의 null을 저장했다. 두 키 모두 get은 null을 반환했지만 containsKey는 각각 false, true였다. 그래서 getOrDefault도 각각 "guest", null을 반환했다.
Map.get 문서 (새 탭에서 열림)도 null 반환만으로 키 부재를 판정할 수 없으며 containsKey로 구분할 수 있다고 설명한다. 자기 코드에서는 같은 Map과 키로, 중간에 데이터가 바뀌지 않는 구간에서 두 결과를 함께 확인한다. get이 null이고 containsKey가 true라면 null 매핑이 있는 것이다.
저장된 null도 기본값으로 바꾸려면
위 JShell 세션에서 다음 두 줄을 이어 실행한다.
String value = names.get("nullValue");
String resolved = value == null ? "guest" : value;
실행 결과는 value ==> null, resolved ==> "guest"였다. 키를 absent로 바꿔 비교해도 resolved는 "guest"였다. 키 부재와 저장된 null을 모두 같은 기본값으로 처리할 때는 이렇게 get의 반환값만 검사하면 된다.
키 부재가 “지정하지 않음”이고 저장된 null이 “명시적으로 비움”이라는 뜻이라면 둘을 합쳐도 되는지 먼저 정한다. null을 호출자에게 남겨야 하면 getOrDefault를 유지하고, 두 경우 모두 기본 이름으로 표시하려면 위의 null 검사를 쓴다. 이 코드는 반환할 문자열만 고르며 Map을 수정하지 않는다. 실제 대체 후에도 names.get("nullValue")는 null, names.containsKey("nullValue")는 true였고, 없던 absent 키도 추가되지 않았다.
빈 문자열은 따로 판단해야 한다. 추가 비교에서 empty 키에 "", nonempty 키에 "Mina"를 저장했다. 두 키 모두 containsKey는 true였고, get·getOrDefault·위의 null 검사 결과는 각각 "", "Mina"를 그대로 유지했다. 화면이 여전히 비어 있다면 값이 null인지 빈 문자열인지 확인한다. 빈 문자열까지 기본값으로 바꿀지는 별도의 입력 규칙이다.
확인을 마치면 JShell에 /exit를 입력해 종료한다. jshell 명령을 찾지 못하면 JDK 설치와 bin 경로를 확인한다. 입력 중 오류가 나면 import와 변수 선언부터 같은 세션에서 실행했는지 본다. 자기 코드에서 키가 없는데도 null이 나온다면 전달한 기본값 자체가 null인지, 같은 Map과 키를 조회했는지도 확인한다.
직접 실행한 범위는 OpenJDK 17.0.2의 단일 스레드와 합성 문자열을 담은 HashMap이다. 다른 JDK 버전·Map 구현과 동시 변경 상황은 실행하지 않았다.