Collectionを変更不可にしよう (Java)

Collections::unmodifiableXXX

Map、Set、ListなどのCollectionを、変更不可なものとして扱うのに
Collections::unmodifiableXXX を使う方法があります。

add, put, remove などの、コレクションの追加、削除などを抑制する事ができます。

Collections::UnmodifiableCollection
Collections::UnmodifiableSet
Collections::UnmodifiableSortedSet
Collections::UnmodifiableNavigableSet
Collections::UnmodifiableList
Collections::UnmodifiableRandomAccessList
Collections::UnmodifiableMap
Collections::UnmodifiableSortedMap
Collections::UnmodifiableNavigableMap

unmodifiable〜 は、これだけありますが、このあたりを使うだけで充分でしょう。

Collections::UnmodifiableSet
Collections::UnmodifiableList
Collections::UnmodifiableMap

使い方

元々の、コレクションを下記のように Collections.unmodifiableList(xxx) を通す事で、
unmodifiableなコレクションにラップして返してくれます。

unmodifiableなコレクションでは、add, remove, put など、コレクションに変更を加える操作が禁止されます。
もしコールすると、UnsupportedOperationException という例外が投げられます。

// List<Hoge> hogeList があるとして

List<Hoge> unmodifiableHogeList = Collections.unmodifiableList(hogeList);

// 参照して使う分には、普通に使える
Hoge hoge = unmodifiableHogeList.get(0); 

// remove, add など、コレクションを変更するメソッドはエラーが発生する。
unmodifiableHogeList.remove(0);  

コレクションはラップされている

unmodifiable系のコレクションは、コレクションコピーを作成しているわけではなく、
元々のコレクションのインスタンスをメンバ変数に保持して、ラップしてるだけです。

UnmodifiableListの実装

static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                              implements List<E> {
    private static final long serialVersionUID = -283967356065247728L;

    final List<? extends E> list;

    UnmodifiableList(List<? extends E> list) {
        super(list);
        this.list = list;
    }

    public boolean equals(Object o) {return o == this || list.equals(o);}
    public int hashCode()           {return list.hashCode();}

    public E get(int index) {return list.get(index);}
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    public int indexOf(Object o)            {return list.indexOf(o);}
    public int lastIndexOf(Object o)        {return list.lastIndexOf(o);}
    public boolean addAll(int index, Collection<? extends E> c) {
            .
            .

コレクションコピーよりスマート

コレクション改変を許容しない場合に、外部にコレクションのインスタンスを渡す場合に、
こんな感じで、コレクションコピーを作成していましたが、

public List<Hoge> hogeList(){
  return new ArrayList<>(_hogeList);
}

Collections.unmodifiableXXXを使う方が、スマートでしょう。

public List<Hoge> hogeList(){
  return Collections.unmodifiableList(_hogeList);
}

コレクションをコピーしている訳ではなくラップしてるだけなので、
無駄なコピーが発生しなくて改変される心配もなくなります。

再帰的にunmodifiableされる訳ではない。

Map構造などで、Map<Integer, Map<Integer, String>> のような
Mapの値が更にMapになっているようなコレクションの場合、
unmodifiableMapを使っても、内側のMapまでがunmodifiableになる訳ではないのでお間違いなく。

二重Mapをつかった、動作サンプルです。

Map<Integer, Map<Integer, String>> map = new HashMap<>();

// 適当に要素を追加
map.put(1, new HashMap<>());
map.get(1).put(1, "1-1");
map.get(1).put(2, "1-2");
map.get(1).put(3, "1-3");

map.put(2, new HashMap<>());
map.get(2).put(1, "2-1");
map.get(2).put(2, "2-2");
map.get(2).put(3, "2-3");


// unmodifiableMap を生成
Map<Integer, Map<Integer, String>>  unmodifiableMap = Collections.unmodifiableMap(map);

// ここはもちろん例外が発生する。
unmodifiableMap.remove(1); 

// ここは内部のMapなので、操作できる。
unmodifiableMap.get(1).remove(1); 

コメントを残す