Javaのプリミティブ配列の不変の代替はありますか?プリミティブ配列を作成final
しても、実際に次のようなことを行うことは妨げられません
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
配列の要素を変更できないようにしたい。
int[]
とnew int[]
比べて入力する方がはるかに簡単ですList<Integer>
とnew ArrayList<Integer>
?XD
Javaのプリミティブ配列の不変の代替はありますか?プリミティブ配列を作成final
しても、実際に次のようなことを行うことは妨げられません
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
配列の要素を変更できないようにしたい。
int[]
とnew int[]
比べて入力する方がはるかに簡単ですList<Integer>
とnew ArrayList<Integer>
?XD
回答:
プリミティブ配列ではありません。リストまたはその他のデータ構造を使用する必要があります。
List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
Arrays.asList
は変更できません。docs.oracle.com/javase/7/docs/api/java/util/… "指定された配列を基にした固定サイズのリストを返します(返されたリストへの変更は、配列に「ライトスルー」されます。)
Arrays.asList()
実際には変更できないように思えるadd
かremove
、返されたアイテムjava.util.Arrays.ArrayList
(と混同しないでください)を変更できないようですjava.util.ArrayList
-これらの操作は実装されていません。たぶん@mauhizはこれを言っているのでしょうか?しかしもちろんList<> aslist = Arrays.asList(...); aslist.set(index, element)
、で既存の要素を変更することができるので、java.util.Arrays.ArrayList
確かに変更不可能ではありません。QEDは、結果asList
と通常の結果の違いを強調するためだけにコメントを追加しましたArrayList
私の推奨は、配列またはを使用するのではなく、この目的のために存在するGuavaのImmutableListunmodifiableList
を使用することです。
ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
Collections.unmodifiableList
、リストを不変にするのに十分ですか?
他の人が指摘したように、Javaで不変の配列を持つことはできません。
元の配列に影響を与えない配列を返すメソッドが絶対に必要な場合は、毎回配列を複製する必要があります。
public int[] getFooArray() {
return fooArray == null ? null : fooArray.clone();
}
明らかにこれはかなり高価です(ゲッターを呼び出すたびに完全なコピーを作成するため)。ただし、インターフェースを変更できず(List
たとえばを使用するため)、クライアントが内部を変更する危険を冒すことができない場合は、必要な場合があります。
この手法は、防御コピーの作成と呼ばれます。
clone()
、Arrays.copy()
ここですか?
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.lang.Integer」ではなくネイティブの「int」が必要な場合は、おそらく独自のラッパークラスを作成する必要があります。ネット上にはさまざまなIntArray実装がありますが、不変なものはありません(私が見つけたもの):Koders IntArray、Lucene IntArray。おそらく他にもあります。
Guava 22以降では、パッケージからcom.google.common.primitives
3つの新しいクラスを使用できます。これらは、と比較してメモリフットプリントが低くなっていますImmutableList
。
彼らもビルダーを持っています。例:
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);
これはラッパーを使用する必要があり、配列ではなくリストですが、取得する最も近いものです。
valueOf()
。オートボクシングが処理します。またArrays.asList(0, 2, 3, 4)
、はるかに簡潔になります。
valueOf()
、内部のIntegerオブジェクトキャッシュを利用してメモリの消費/リサイクルを削減することです。
int
にInteger
かかわらを。逆に注意する必要があります。
Java9のof(E ... elements)メソッドを使用すると、次の行だけで不変のリストを作成できます。
List<Integer> items = List.of(1,2,3,4,5);
上記のメソッドは、任意の数の要素を含む不変のリストを返します。そして、このリストに整数を追加すると、java.lang.UnsupportedOperationException
例外が発生します。このメソッドは、引数として単一の配列も受け入れます。
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()));
完璧ではありませんが、少なくとも(クラスの観点から)「疑似不変配列」があり、これにより関連コードが壊れることはありません。
まあ..配列は、バリアントパラメータとして定数として渡すのに役立ちます(もしそうであれば)。
int[]
呼び出し元は、APIの内部に影響を与えることなく、その配列を使用して必要なことを実行できると想定する場合があります。