본문 바로가기
Language/Java

[JAVA] Java 컬렉션 프레임워크와 null 허용성 : 안정적인 코딩을 위한 가이드

by Papa Martino V 2026. 1. 18.
728x90

컬렉션 프레임워크와 null 허용성
컬렉션 프레임워크와 null 허용성

 

Java 개발자라면 누구나 한 번쯤 NullPointerException(NPE)의 늪에 빠져본 경험이 있을 것입니다. 특히 데이터를 그룹화하여 관리하는 컬렉션 프레임워크(Collection Framework)를 다룰 때, 특정 클래스가 null을 허용하는지 여부를 정확히 아는 것은 코드의 안정성을 결정짓는 핵심적인 요소입니다. 단순히 "어떤 클래스가 null을 허용한다"는 지식을 넘어, 어떤 클래스는 허용하고 어떤 클래스는 금지하는지, 그리고 실무에서 이를 어떻게 전략적으로 선택해야 하는지에 대한 깊이 있는 통찰을 공유하고자 합니다.


1. Java 컬렉션의 null 허용 여부 개요

Java의 java.util 패키지 내 주요 컬렉션들은 각자의 설계 철학에 따라 null 요소를 저장할 수 있는 능력이 다릅니다. 이는 내부 데이터 구조(Hash, Tree 등)와 동기화 정책(Thread-safe 여부)에 기인합니다.

 

주요 인터페이스별 null 허용 특징

인터페이스 구현 클래스 null 허용 여부 비고 (주의사항)
List ArrayList 허용 중복 null 삽입 가능
LinkedList 허용 노드 내 데이터로 null 저장
Set HashSet 허용 (1개) 내부적으로 HashMap의 Key로 관리
LinkedHashSet 허용 (1개) 삽입 순서 유지하며 null 허용
TreeSet 불허 정렬 시 비교 연산으로 인한 NPE 발생
Map HashMap Key/Value 모두 허용 Key는 1개만 null 가능
LinkedHashMap Key/Value 모두 허용 순서 보장형 HashMap
TreeMap Key 불허 / Value 허용 정렬 기준(Comparable) 필요
Hashtable 불허 Legacy 클래스, 원천적 차단
Concurrent ConcurrentHashMap 불허 원자성 및 동시성 제어 목적

2. 왜 어떤 컬렉션은 null을 거부하는가?

2.1 정렬(Ordering)과 비교(Comparison)

TreeSetTreeMap은 내부적으로 요소를 정렬된 상태로 유지합니다. 이를 위해 compareTo() 또는 Comparator를 사용합니다. null은 다른 객체와 크기를 비교할 수 없으므로, 비교 연산을 시도하는 즉시 NullPointerException을 던집니다. 이는 데이터의 일관성을 유지하기 위한 엄격한 설계입니다.

2.2 동시성 제어(Concurrency)와 모호함 제거

가장 흥미로운 부분은 ConcurrentHashMap입니다. 창시자인 Doug Lea에 따르면, 멀티스레드 환경에서 get(key)null을 반환했을 때, 이것이 '값이 null인 것'인지 '값이 존재하지 않는 것'인지 구분하기 어렵기 때문에 의도적으로 null을 금지했습니다. 일반 HashMapcontains(key)로 체크가 가능하지만, 멀티스레드 상황에서는 체크와 호출 사이에 상태가 변할 수 있어(Race Condition) null을 원천 차단하는 방식을 택했습니다.


3. 실무에서의 전략적 선택 가이드

"가급적 컬렉션에 null을 넣지 않는 것이 최선입니다."

모던 자바(Java 8+)에서는 Optional이라는 강력한 도구가 등장했습니다. 컬렉션 내부 요소로 null을 허용하기보다는 다음과 같은 대안을 고려해야 합니다.

  • Optional 사용: 값이 없을 수 있음을 명시적으로 표현합니다.
  • Empty Collection 반환: 메서드 결과로 null 리스트 대신 Collections.emptyList()를 반환하여 호출 측의 NPE 위험을 줄입니다.
  • Null Object Pattern: '아무것도 하지 않는' 객체를 정의하여 로직의 흐름을 끊지 않도록 합니다.

4. 결론 및 요약

Java 컬렉션 프레임워크에서 null 허용 여부는 단순한 기능 차이가 아니라 데이터 구조의 목적과 안전성에 직결됩니다. ArrayListHashMap처럼 유연함이 필요한 곳에서는 null을 허용하지만, 정렬이 필요한 TreeMap이나 멀티스레드 안전성이 중요한 ConcurrentHashMap에서는 엄격히 제한됩니다. 안정적인 애플리케이션을 구축하기 위해서는 사용하는 컬렉션의 특성을 정확히 이해하고, null을 다루는 방어적인 코딩 습관을 갖추는 것이 필수적입니다.


참고 문헌 및 출처

  • Oracle Java Documentation: The Java™ Tutorials - Collections Framework
  • Effective Java 3rd Edition (Joshua Bloch)
  • JSR 166: Concurrency Utilities (Doug Lea's Design Notes)
  • Java SE Specification: Section 20: Package java.util
728x90