エスプリフォートではただ言語を学ぶだけでなく、積極的に新しい事を取り入れ、保守性、可読性なども考慮したプログラミングを心がけています。
これら磨きあげた技術力が、お客様に高いクオリティでシステムを提供する一助になっています。
はじめに
JAVAでコーディングをする上で、リスト(List)や連想配列(Map)等のコレクションは切り離せないものになっています。
このコレクションの一つに「不変(immutable)コレクション」が追加されました。
こちらは、Java8以降からサポートされています。
この「不変(immutable)コレクション」の特徴をしっかりおさえて効果的な使い方をすれば、コーディング時の間違いや想定外の使われ方などが入り込む余地を少なくでき、エンジニアとしての技術力が一段高くなることも間違いなし!
ということで、今回は「不変(immutable)コレクション」について説明していきましょう!
不変(immutable)コレクションって何?
不変(immutable)という言葉の通り、変更されないコレクションです。
読み込み専用のコレクション、とも言えますね。
どういう時に使えば良いの?
「変更されることがない」、特に「変更されることを許可しない」コレクションを扱う場合に使用します。
不変(immutable)コレクションをうまく適用することにより、コーディング時の間違いや想定外が入り込む余地を少なくでき、予期しない不具合を防ぐことができるようになります。
例として、「あるクラスにあるコレクションを取り出し、そのまま出力する」という実装を考えてみましょう。
まずは、不変(immutable)コレクションを使用しない実装です。
言語は、Javaを使用します。
不変コレクションを使用しない場合
import java.util.ArrayList;
import java.util.List;
public class SampleModifiableCollection {
private List<String> users = new ArrayList<String>();
public SampleModifiableCollection() {
users.add("太郎");
users.add("次郎");
users.add("花子");
}
public List<String> getUsers() {
return users;
}
public static void main(String[] args) {
SampleModifiableCollection c = new SampleModifiableCollection();
var users = c.getUsers();
for (String user : users) {
System.out.println(user);
}
}
}
特に問題のないような実装に見えます。
実際、正常に動作します。
ここで、mainクラスを以下の通りに誤って変更してしまった、とします。
public static void main(String[] args) {
SampleUnModifiableCollection c = new SampleUnModifiableCollection();
var users = c.getUsers();
// ↓ 変更:usersに三郎さんを追加
// ↓ 本来、ここで追加してはいけない
users.add("三郎");
// ↑
for (String user : users) {
System.out.println(user);
}
}
SampleModifiableCollectionのインスタンスから取り出したusersに対して、mainメソッドから変更(今回の場合は"三郎"さんの追加)が出来てしまいました。
もちろん、正常に動作します。
どうすればよい?
『そんなの、プログラマが実装時に変更しないように注意すれば良い』と、思うかもしれません。
しかし、如何に注意をしていても、間違ってしまうことはあります。
では、どうすればよいでしょうか?
何か他に良い方法はないものでしょうか?
不変(immutable)コレクションを使おう
『そもそも他から変更できるようにしなければ良い』がその答えの一つとなります。
これを実現するのが不変コレクションです。
それでは、不変(immutable)コレクションを使用した実装です(mainクラスは、先ほどのサンプル実装をそのまま流用します)。
不変(immutable)コレクションを使用した場合
import java.util.ArrayList;
// ↓ 変更:コレクションをインポート
import java.util.Collections;
import java.util.List;
public class SampleUnModifiableCollection {
private List<String> users = new ArrayList<String>();
public SampleUnModifiableCollection() {
users.add("太郎");
users.add("次郎");
users.add("花子");
}
public List<String> getUsers() {
// ↓ 変更:不変コレクションとして返却
return Collections.unmodifiableList(users);
}
public static void main(String[] args) {
SampleUnModifiableCollection c = new SampleUnModifiableCollection();
var users = c.getUsers();
// ↓ 変更:usersに三郎さんを追加
// ↓ 本来、ここで追加してはいけない
users.add("三郎");
// ↑
for (String user : users) {
System.out.println(user);
}
}
}
これは、正常動作しません。
users.add("三郎");
の実行時に、UnSupportedOperationExceptionの例外が発生します。
例外が発生した理由は、SampleUnModifiableCollectionのインスタンスから取得したusersが不変(immutable)コレクションだからです。
java.util.Collectionsクラス自体はコレクションを扱う役割を持ち、そのstaticなメソッドであるunmodifiableList()を使用する事で、不変(immutable)コレクションとしてusersに返却しています。
このようにして、不変(immutable)コレクションは他から変更することができないようにできます。
Javaのバージョンで異なる変更不可なコレクションを作成するメソッド
Javaのバージョンにより、使える変更不可なコレクションを作成するメソッドが追加されています。
■ List
List<Integer> intList = new ArrayList<>();
intList.add(100);
intList.add(200);
intList.add(300);
List<Integer> unmodList = Collections.unmodifiableList(intList);
List<Integer> unmodList = List.of(1,2,3);
List<Integer> intList = new ArrayList<>();
intList.add(100);
intList.add(200);
intList.add(300);
List<Integer> unmodList = List.copyOf(intList);
■ Map
Map<String, Integer> stringMap = new HashMap<String, Integer>();
stringMap.put("aa", 100);
stringMap.put("bb", 200);
stringMap.put("cc", 300);
Map<String, Integer> unmodMap = Collections.unmodifiableMap(stringMap);
Map<String, Integer> unmodMap = Map.of("aa", 100, "bb", 200, "cc", 300);
Map<String, Integer> stringMap = new HashMap<String, Integer>();
stringMap.put("aa", 100);
stringMap.put("bb", 200);
stringMap.put("cc", 300);
Map<String, Integer> unmodMap = Map.copyOf(stringMap);
■ Set
Set<String> stringSet = new HashSet<>(Arrays.asList("aa", "bb", "cc"));
Set<String> unmodSet = Collections.unmodifiableSet(stringSet);
Set<String> unmodSet = Set.of("aa", "bb", "cc");
Set<String> stringSet = new HashSet<>(Arrays.asList("aa", "bb", "cc"));
Set<String> unmodSet = Set.copyOf(stringSet);
不変(immutable)コレクションを扱う際の注意点
1、並び替えが出来ない
不変(immutable)コレクションは、後から並べ替えはできません。
List<Integer> list = List.of(10,9,7,8,5,2,6,4,3,1);
Collections.sort(list); // UnsupportedOperationExceptionが発生
このように不変(immutable)コレクションとして生成した後に並び替えようとしても、例外が発生して並び替えることはできません。
2、不変(immutable)コレクション内のValue Objectには不変(immutable)は適用されない
不変(immutable)コレクションにValue Objectが格納されている場合は、コレクションに追加、削除、変更、並び替えという状態を変更することができないが、コレクションに格納されているValue Objectの中の値は変更できます。
もし、変更したくない場合は、自分でimmutableなValue Objectを作る必要があります。
まとめ
チームでシステム開発する際には、共通機能などを意図しない使い方をしようとする方もいます。
また良かれと思い、本来は追加してはいけないリストにデータを追加する等、多くのエンジニアが集まって開発すると共通機能などを作ったエンジニアが想定した斜め上のコーディングをされることが起こりやすくなります。
そんなときに、不変(immutable)コレクションを活用すれば、コーディング時の間違いや想定外の使われ方などが入り込む余地を少なくできます。
ぜひ、不変(immutable)コレクションを上手く適用していきましょう!