イミュータブルとは何か
説明
イミュータブル(immutable)とは、「不変」という意味です。
イミュータブルオブジェクトは、インスタンス作成後に内部状態を変更できないオブジェクトを指します。
一度値が設定されると、その値が変更されることがなく、新たに設定し直すこともできません。
例
- boolean、int等のプリミティブ型
- String、Integer等のラッパークラス
- java.util.Collectionsの不変コレクション
などがあります。
特徴
- 初期化時にのみ値を設定できる
- 設定された値は変更不可
- 値を変更する操作は新しいオブジェクトを生成する
対比
イミュータブルに対して、ミュータブル(mutable)オブジェクトは、その内部状態を自由に変更できるオブジェクトです。ArrayListやStringBuilder、Calendarなどがミュータブルオブジェクトです。
なぜイミュータブルオブジェクトを使うのか
イミュータブルオブジェクトを使用する主な理由には以下のものがあります。
1. スレッドセーフ
イミュータブルオブジェクトは、その状態が変更されないため、複数のスレッドから同時にアクセスされてもデータ競合が発生しません。これにより、マルチスレッド環境での安全性が保証されます。
例えば、スレッド間で共有されるマップやリストなどは、イミュータブルなオブジェクトとして実装するのが一般的です。
2. シンプルなデバッグ
状態が変わらないため、イミュータブルオブジェクトは予測可能であり、バグの追跡が容易です。ある時点でのオブジェクトの状態がその後変わることがないため、一度状態を確認すれば、それ以降その状態について心配する必要がありません。
3. キャッシュの効率化
イミュータブルオブジェクトは変更されないため、キャッシュに保存しても一貫性が保たれます。
キャッシュキーは、キャッシュされた値と一意に結びついているため、変更されないことが重要です。イミュータブルなオブジェクトを使用することで、キャッシュキーの整合性を保ち、キャッシュのヒット率を向上させることができます。これにより、キャッシュの利用効率が向上し、パフォーマンスの改善につながります。
イミュータブルオブジェクトはどう動くのか
一般的に、イミュータブルオブジェクトはコピーオンライト方式と呼ばれる仕組みで実装されます。これは、オブジェクトへの変更操作を行う際、既存のオブジェクトを変更するのではなく、新しいオブジェクトを生成することで実現する手法です。
イミュータブル(String)
Stringはイミュータブル(不変)オブジェクトです。つまり、一度作成されたStringオブジェクトの内容は変更できません。文字列を結合すると、新しいStringオブジェクトが作成されます。
public class StringExample {
public static void main(String[] args) {
String str = "Hello";
str += " ";
str += "World";
System.out.println(str); // 出力: Hello World
}
}
上記のコードでは、文字列の結合が行われるたびに新しいStringオブジェクトが作成されます。これは、小規模な文字列結合では問題ありませんが、大規模な結合ではメモリとパフォーマンスに悪影響を及ぼす可能性があります。
ミュータブル(StringBuilder)
StringBuilderはミュータブル(可変)オブジェクトで、内部の文字列を変更することができます。文字列を結合する際に、新しいオブジェクトを作成せずに内部バッファを使用して効率的に操作を行います。
public class StringBuilderExample {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println(sb.toString()); // 出力: Hello World
}
}
上記のコードでは、StringBuilderを使用して文字列を結合しています。内部バッファを利用することで、Stringのように新しいオブジェクトを作成することなく効率的に文字列結合が行われます。
コピーオンライト方式のメリット
- スレッドセーフ: 複数のスレッドからオブジェクトに同時にアクセスしても、データ競合が発生しない。
- メモリ効率: 多くの場合、変更頻度が低いオブジェクトを効率的に共有できる。
- 単純な実装: オブジェクトをコピーするだけのシンプルな実装が可能。
コピーオンライト方式のデメリット
- メモリ使用量の増加: オブジェクトのコピーが生成されるため、メモリ使用量が増加する可能性がある。
- パフォーマンスの低下: オブジェクトのコピーが発生するたびに、パフォーマンスが低下する可能性がある。
補足
Java 8以降では、Stringクラスにもappend()メソッドが追加されています。少量の文字列結合を行う場合は、append()メソッドを使用することで、Stringオブジェクトを直接操作できます。