Java中的不可变数组

时间:2010-09-13 13:43:13

标签: java arrays immutability

Java中的原始数组是否有不可变的替代方法?制作一个原始数组final实际上并不能阻止人们做类似

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

我希望数组的元素不可更改。

14 个答案:

答案 0 :(得分:153)

不使用原始数组。您需要使用List或其他一些数据结构:

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

答案 1 :(得分:69)

我的建议是不使用数组或unmodifiableList,而是使用GuavaImmutableList,这是为此目的而存在的。

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

答案 2 :(得分:20)

正如其他人所说,你不能在Java中拥有不可变数组。

如果您绝对需要一个返回不影响原始数组的数组的方法,那么每次都需要克隆该数组:

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

显然这是相当昂贵的(因为每次调用getter时都会创建一个完整的副本),但如果你不能更改界面(例如使用List)而不能客户改变你的内部风险,然后可能是必要的。

这种技术被称为制作防御性副本。

答案 3 :(得分:14)

有一种方法可以在Java中创建不可变数组:

final String[] IMMUTABLE = new String[0];

具有0个元素的阵列(显然)不能变异。

如果您使用List.toArray方法将List转换为数组,这实际上可以派上用场。因为即使空数组占用一些内存,您也可以通过创建一个常量空数组来保存该内存分配,并始终将其传递给toArray方法。如果你传递的数组没有足够的空间,那么该方法将分配一个新数组,但如果确实如此(列表为空),它将返回你传递的数组,允许你在任何时候调用{{} { 1}}空toArray

List

答案 4 :(得分:5)

另一个答案

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 :(得分:4)

从Java 9开始,您可以使用List.of(...)JavaDoc

此方法返回不可变List并且非常有效。

答案 6 :(得分:3)

如果您需要(出于性能原因或节省内存)原生'int'而不是'java.lang.Integer',那么您可能需要编写自己的包装类。网上有各种IntArray实现,但没有(我发现)是不可变的:Koders IntArrayLucene IntArray。可能还有其他人。

答案 7 :(得分:2)

从Guava 22开始,从包com.google.common.primitives开始,您可以使用三个新类,与ImmutableList相比,内存占用更少。

他们也有一个建设者。例如:

int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
  .add(1L)
  .add(2L)
  .build();

或者,如果在编译时知道大小:

ImmutableLongArray longArray = ImmutableLongArray.of(1L, 2L);

这是获取Java基元的数组的不可变视图的另一种方法。

答案 8 :(得分:1)

不,这是不可能的。但是,人们可以这样做:

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

这需要使用包装器,它是一个List,而不是一个数组,但是它是你最接近的。

答案 9 :(得分:1)

在某些情况下,使用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})

答案 10 :(得分:1)

如果你想避免可变性和拳击,没有办法开箱即用。但是你可以创建一个包含原始数组的类,并通过方法提供对元素的只读访问。

答案 11 :(得分:0)

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

不完美,但至少你有“伪不可变数组”(从类角度来看),这不会破坏相关代码。

答案 12 :(得分:0)

Java9 中的of(E... elements)方法可用于仅使用一行创建不可变列表:

int cmp3(int *a, int *b) {
    return cmp2(a, b, 2);
} 

上述方法返回包含任意数量元素的不可变列表。在此列表中添加任何整数都会导致List<Integer> items = List.of(1,2,3,4,5); 异常。此方法还接受单个数组作为参数。

java.lang.UnsupportedOperationException

答案 13 :(得分:-3)

嗯..数组作为常量传递(如果它们)作为变量参数很有用。