Java | Java 標準ライブラリ:コレクションの null 許容

Java Java
スポンサーリンク

「コレクションは null を入れていいのか?」という全体像

Java のコレクションで初心者が一番ハマるポイントの一つが、

「null を入れていいのか・ダメなのかがバラバラ」

ということです。

ArrayList には null を入れても動く
HashMap には null キーも null 値も入る
TreeMapTreeSet の「キーや要素の null」はダメ

といったように、実装クラスごとにルールが違います。

まずは「どのコレクションが null を許すのか」という全体像を整理し、そのあとで「そもそも設計として null をどう扱うべきか」を掘り下げます。


List 系(ArrayList / LinkedList)の null 許容

List は null 要素を「普通に」持てる

List(特に ArrayListLinkedList)は、基本的に 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
Java

List 自体は null を許しても、
「その後の処理が null を前提にしていない」と簡単に落ちます。

だから、

List に null を入れても技術的には問題ない
けれど、取り出す側で null をどう扱うかが常に問題になる

この点を強く意識しておく必要があります。


Set 系(HashSet / LinkedHashSet / TreeSet)の null 許容

HashSet / LinkedHashSet は null 要素を 1 つだけ持てる

HashSetLinkedHashSet は、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個だけ)
    }
}
Java

null の重複は許されないので、結果として

「Set の中に null が 0 個か 1 個だけ存在しうる」

という状態になります。

TreeSet は null をそもそも許さない

TreeSet は「順序付き Set」でした。内部的にはソート済みの木構造(たとえば赤黒木)を使っており、要素同士を順序比較(compareToComparator)します。

null を入れようとすると、比較ができないため NullPointerExceptionClassCastException につながります。そのため 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

HashMapLinkedHashMap は、キーにも値にも 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 を入れない方が安全か?」を最初に考える。

タイトルとURLをコピーしました