web-dev-qa-db-ja.com

Javaの不変配列

Javaのプリミティブ配列の不変の代替手段はありますか?プリミティブ配列finalを作成しても、実際に次のようなことを防ぐことはできません。

final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;

配列の要素を変更できないようにします。

147
ignoramus

プリミティブ配列ではありません。リストまたはその他のデータ構造を使用する必要があります。

List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
157
Jason S

私の推奨事項は、配列またはunmodifiableListを使用せず、この目的のために存在する GuavaImmutableList を使用することです。

ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
71
ColinD

他の人が指摘したように、Javaで不変配列を持つことはできません。

元の配列に影響を与えない配列を返すメソッドが絶対に必要な場合は、毎回配列を複製する必要があります。

public int[] getFooArray() {
  return fooArray == null ? null : fooArray.clone();
}

明らかにこれはかなり高価です(ゲッターを呼び出すたびに完全なコピーを作成するため)が、インターフェイスを変更できない場合(たとえばListを使用するため)、クライアントが内部を変更するリスクがない場合、それが必要な場合があります。

この手法は、防御コピーの作成と呼ばれます。

20
Joachim Sauer

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
14
Brigham

もう一つの答え

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"});
}
5
aeracode

Java 9以降、List.of(...)JavaDoc を使用できます。

このメソッドは、不変のListを返し、非常に効率的です。

4
rmuller

(パフォーマンス上の理由またはメモリを節約するために)「Java.lang.Integer」ではなくネイティブの「int」が必要な場合は、おそらく独自のラッパークラスを記述する必要があります。ネット上にはさまざまなIntArrayの実装がありますが、不変(私が見つけた)はありませんでした: Koders IntArrayLucene IntArray 。おそらく他にもあります。

3
Thomas Mueller

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プリミティブの配列の不変ビューを取得する別の方法です。

2
JeanValjean

いいえ、これは不可能です。ただし、次のようなことができます。

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);

これにはラッパーを使用する必要があります。これは配列ではなくリストですが、最も近いものです。

1
user439793

状況によっては、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})
1
kevinarpe

可変性とボックス化の両方を回避したい場合、すぐに使用できる方法はありません。ただし、内部にプリミティブ配列を保持し、メソッドを介して要素への読み取り専用アクセスを提供するクラスを作成できます。

1
Sarge Borsch

Java9of(E ... elements) メソッドを使用すると、1行だけで不変リストを作成できます。

List<Integer> items = List.of(1,2,3,4,5);

上記のメソッドは、任意の数の要素を含む不変のリストを返します。このリストに整数を追加すると、Java.lang.UnsupportedOperationExceptionexceptionになります。このメソッドは、引数として単一の配列も受け入れます。

String[] array = ... ;
List<String[]> list = List.<String[]>of(array);
0
i_am_zero

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()));

完全ではありませんが、少なくとも(クラスの観点から)「疑似不変配列」があり、これは関連するコードを壊しません。

0
miguelt