Javaのプリミティブ配列の不変の代替手段はありますか?プリミティブ配列final
を作成しても、実際に次のようなことを防ぐことはできません。
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
配列の要素を変更できないようにします。
プリミティブ配列ではありません。リストまたはその他のデータ構造を使用する必要があります。
List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
私の推奨事項は、配列またはunmodifiableList
を使用せず、この目的のために存在する Guava の ImmutableList を使用することです。
ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
他の人が指摘したように、Javaで不変配列を持つことはできません。
元の配列に影響を与えない配列を返すメソッドが絶対に必要な場合は、毎回配列を複製する必要があります。
public int[] getFooArray() {
return fooArray == null ? null : fooArray.clone();
}
明らかにこれはかなり高価です(ゲッターを呼び出すたびに完全なコピーを作成するため)が、インターフェイスを変更できない場合(たとえばList
を使用するため)、クライアントが内部を変更するリスクがない場合、それが必要な場合があります。
この手法は、防御コピーの作成と呼ばれます。
Javaで不変配列を作成する方法が1つあります。
final String[] IMMUTABLE = new String[0];
(明らかに)要素が0の配列は変更できません。
List.toArray
メソッドを使用してList
を配列に変換する場合、これは実際に便利です。空の配列でもメモリを消費するため、定数の空の配列を作成し、常にtoArray
メソッドに渡すことで、そのメモリ割り当てを節約できます。そのメソッドは、渡す配列に十分なスペースがない場合、新しい配列を割り当てますが、もしそれがあれば(リストが空の場合)、渡した配列を返し、toArray
を呼び出すたびにその配列を再利用できる空のList
。
final static String[] EMPTY_STRING_ARRAY = new String[0];
List<String> emptyList = new ArrayList<String>();
return emptyList.toArray(EMPTY_STRING_ARRAY); // returns EMPTY_STRING_ARRAY
もう一つの答え
static class ImmutableArray<T> {
private final T[] array;
private ImmutableArray(T[] a){
array = Arrays.copyOf(a, a.length);
}
public static <T> ImmutableArray<T> from(T[] a){
return new ImmutableArray<T>(a);
}
public T get(int index){
return array[index];
}
}
{
final ImmutableArray<String> sample = ImmutableArray.from(new String[]{"a", "b", "c"});
}
Java 9以降、List.of(...)
、 JavaDoc を使用できます。
このメソッドは、不変のList
を返し、非常に効率的です。
(パフォーマンス上の理由またはメモリを節約するために)「Java.lang.Integer」ではなくネイティブの「int」が必要な場合は、おそらく独自のラッパークラスを記述する必要があります。ネット上にはさまざまなIntArrayの実装がありますが、不変(私が見つけた)はありませんでした: Koders IntArray 、 Lucene IntArray 。おそらく他にもあります。
Guava 22以降、パッケージcom.google.common.primitives
から、ImmutableList
と比較してメモリフットプリントが低い3つの新しいクラスを使用できます。
また、ビルダーもあります。例:
int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
.add(1L)
.add(2L)
.build();
または、コンパイル時にサイズがわかっている場合:
ImmutableLongArray longArray = ImmutableLongArray.of(1L, 2L);
これは、Javaプリミティブの配列の不変ビューを取得する別の方法です。
いいえ、これは不可能です。ただし、次のようなことができます。
List<Integer> temp = new ArrayList<Integer>();
temp.add(Integer.valueOf(0));
temp.add(Integer.valueOf(2));
temp.add(Integer.valueOf(3));
temp.add(Integer.valueOf(4));
List<Integer> immutable = Collections.unmodifiableList(temp);
これにはラッパーを使用する必要があります。これは配列ではなくリストですが、最も近いものです。
状況によっては、Google Guavaライブラリの静的メソッドList<Integer> Ints.asList(int... backingArray)
を使用する方が軽量です。
例:
List<Integer> x1 = Ints.asList(0, 1, 2, 3)
List<Integer> x1 = Ints.asList(new int[] { 0, 1, 2, 3})
可変性とボックス化の両方を回避したい場合、すぐに使用できる方法はありません。ただし、内部にプリミティブ配列を保持し、メソッドを介して要素への読み取り専用アクセスを提供するクラスを作成できます。
Java9の of(E ... elements) メソッドを使用すると、1行だけで不変リストを作成できます。
List<Integer> items = List.of(1,2,3,4,5);
上記のメソッドは、任意の数の要素を含む不変のリストを返します。このリストに整数を追加すると、Java.lang.UnsupportedOperationException
exceptionになります。このメソッドは、引数として単一の配列も受け入れます。
String[] array = ... ;
List<String[]> list = List.<String[]>of(array);
Collections.unmodifiableList()
が機能するのは事実ですが、配列を返すように既に定義されたメソッドを持つ大きなライブラリがある場合があります(例:String[]
)。それらを壊さないように、実際に値を保存する補助配列を定義できます:
public class Test {
private final String[] original;
private final String[] auxiliary;
/** constructor */
public Test(String[] _values) {
original = new String[_values.length];
// Pre-allocated array.
auxiliary = new String[_values.length];
System.arraycopy(_values, 0, original, 0, _values.length);
}
/** Get array values. */
public String[] getValues() {
// No need to call clone() - we pre-allocated auxiliary.
System.arraycopy(original, 0, auxiliary, 0, original.length);
return auxiliary;
}
}
テストする:
Test test = new Test(new String[]{"a", "b", "C"});
System.out.println(Arrays.asList(test.getValues()));
String[] values = test.getValues();
values[0] = "foobar";
// At this point, "foobar" exist in "auxiliary" but since we are
// copying "original" to "auxiliary" for each call, the next line
// will print the original values "a", "b", "c".
System.out.println(Arrays.asList(test.getValues()));
完全ではありませんが、少なくとも(クラスの観点から)「疑似不変配列」があり、これは関連するコードを壊しません。