「コレクションは null を入れていいのか?」という全体像
Java のコレクションで初心者が一番ハマるポイントの一つが、
「null を入れていいのか・ダメなのかがバラバラ」
ということです。
ArrayList には null を入れても動くHashMap には null キーも null 値も入るTreeMap や TreeSet の「キーや要素の null」はダメ
といったように、実装クラスごとにルールが違います。
まずは「どのコレクションが null を許すのか」という全体像を整理し、そのあとで「そもそも設計として null をどう扱うべきか」を掘り下げます。
List 系(ArrayList / LinkedList)の null 許容
List は null 要素を「普通に」持てる
List(特に ArrayList や LinkedList)は、基本的に null を要素として普通に扱えます。
import java.util.ArrayList;
import java.util.List;
public class ListNullSample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Alice");
list.add(null); // null を追加
list.add("Bob");
System.out.println(list.get(1)); // null
System.out.println(list); // [Alice, null, Bob]
}
}
Javaコンパイルも実行も問題なく通ります。get(1) の戻り値が null であることを、呼び出し側がちゃんと意識していれば OK です。
ただし「取り出してすぐメソッド呼び」は危険
こういうコードは危ないです。
String s = list.get(1);
System.out.println(s.length()); // s が null のとき NullPointerException
JavaList 自体は null を許しても、
「その後の処理が null を前提にしていない」と簡単に落ちます。
だから、
List に null を入れても技術的には問題ない
けれど、取り出す側で null をどう扱うかが常に問題になる
この点を強く意識しておく必要があります。
Set 系(HashSet / LinkedHashSet / TreeSet)の null 許容
HashSet / LinkedHashSet は null 要素を 1 つだけ持てる
HashSet や LinkedHashSet は、Set なので「同じ要素は 1 つだけ」でした。
null に対しても同じルールです。
import java.util.HashSet;
import java.util.Set;
public class SetNullSample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Alice");
set.add(null);
set.add(null); // 2回目の null(重複扱い)
System.out.println(set); // [null, Alice] など(null は1個だけ)
}
}
Javanull の重複は許されないので、結果として
「Set の中に null が 0 個か 1 個だけ存在しうる」
という状態になります。
TreeSet は null をそもそも許さない
TreeSet は「順序付き Set」でした。内部的にはソート済みの木構造(たとえば赤黒木)を使っており、要素同士を順序比較(compareTo や Comparator)します。
null を入れようとすると、比較ができないため NullPointerException や ClassCastException につながります。そのため TreeSet は null 要素を禁止しています。
import java.util.Set;
import java.util.TreeSet;
public class TreeSetNullSample {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("Alice");
set.add(null); // 実行時に例外になる可能性大
}
}
Java「順序付き」のコレクションは、基本的に null を嫌う、と覚えておくと整理しやすいです。
Map 系(HashMap / LinkedHashMap / TreeMap)の null 許容
HashMap / LinkedHashMap は「null キー 1 個」「null 値 複数」OK
HashMap や LinkedHashMap は、キーにも値にも null を許します。
import java.util.HashMap;
import java.util.Map;
public class HashMapNullSample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put(null, "NULL_KEY"); // null キー
map.put("A", null); // null 値
map.put("B", "value");
System.out.println(map.get(null)); // NULL_KEY
System.out.println(map); // {null=NULL_KEY, A=null, B=value} など
}
}
Javaただし、null キーは一意なので、二度目以降の map.put(null, ...) は上書きになります。
値の null は重複して何個あっても構いません。
TreeMap の null キーは禁止(値の null は実装によるが避けるべき)
TreeMap は「キーでソートされる Map」です。TreeSet と同様、キー同士を比較して順序を決めるため、null キーは比較不能です。
そのため、TreeMap に null キーを入れると、多くの場合 NullPointerException が投げられます。
import java.util.Map;
import java.util.TreeMap;
public class TreeMapNullSample {
public static void main(String[] args) {
Map<String, String> map = new TreeMap<>();
map.put(null, "NG"); // 実行時に例外
}
}
Java値に null を入れること自体は技術的には可能な場合もありますが、
キーに対する比較や範囲検索が主役の構造なので、実務的には「TreeMap に null は基本入れない」で覚えておく方が安全です。
「null を許すかどうか」の設計の話(ここが本当に重要)
技術的には「入る・入らない」がコレクションごとに決まっていますが、
実務レベルでは、
「そもそもコレクションに null を入れる設計にするか?」
を、ちゃんと自分で決める必要があります。
設計としては「できるだけ null を入れない」方が安全
例えば List<String> に null を入れると、
get した側は、毎回 null チェックを意識しなければいけないstream() で扱うときに、map(s -> s.toUpperCase()) のような処理で簡単に落ちる
といった面倒が発生します。
だから、現場の設計方針としては、
- 「コレクションの中身は原則 null 禁止」
- 「null になりうるものは Optional や別のフラグで扱う」
- 「そもそもその要素をコレクションに入れない(存在しないことを意味にする)」
などに寄せることが多いです。
たとえば、「まだ値が決まっていないユーザー」を表したいなら、
List<User> の中に null を入れるのではなくUser のフィールド(メールアドレスなど)を null 許容にする、あるいは Optional<String> にする
といった設計の方が扱いやすくなります。
Map の null キー・null 値は特に慎重に
HashMap は null キーも null 値も許しますが、次のような問題があります。
存在しないキーを get すると null
「キーはあるが、値が本当に null」の場合も null
ここが区別しづらいのが本当に厄介です。
そのため、
- 「キーに null を使わない」
- 「値に null を使うのも極力避ける(意味があるなら Optional などで表す)」
という方針にしておくと、後々のバグが減ります。
まとめ:コレクションと null を頭の中でこう整理する
最後に、初心者向けの覚え方として整理します。
- List(ArrayList / LinkedList)は null 要素を持てる。ただし取り出し側の null チェックが必須。
- HashSet / LinkedHashSet は null 要素を 1 個だけ持てる。TreeSet は null 要素を許さない。
- HashMap / LinkedHashMap は「null キー 1 個」「null 値は複数 OK」。TreeMap は null キー禁止。
- 順序付き(TreeSet / TreeMap)のコレクションは、比較の都合上 null を基本的に嫌う。
- 設計としては「入るから入れる」ではなく、「そもそもコレクションに null を入れない方が安全か?」を最初に考える。
