Java中的不可变数组

Java中的原始数组有没有不可替代的select? 做一个基本的数组final实际上并不能阻止他们做类似的事情

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

我想数组的元素是不可改变的。

不与原始数组。 你将需要使用一个列表或其他数据结构:

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

我的build议是不使用数组或不可unmodifiableList而是使用Guava的ImmutableList ,它是为此目的而存在的。

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

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

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

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

很明显,这个代价相当昂贵(因为每次调用getter的时候都会创build一个完整的副本),但是如果你不能改变接口(例如使用一个List )并且不能冒着改变你的内部的风险,那么这可能是必要的。

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

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

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

有0个元素的数组(显然)不能被突变。

如果使用List.toArray方法将List转换为数组,实际上可以派上用场。 由于即使是空数组也会占用一些内存,所以可以通过创build一个常量空数组来保存内存分配,并将其传递给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> { 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"}); } 

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

自从包com.google.common.primitives Guava 22以来,可以使用三个新的类,与ImmutableList相比,这三个类具有较低的内存占用。

  • ImmutableIntArray
  • ImmutableLongArray
  • ImmutableDoubleArray

他们也有一个build设者。 例:

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

如果你想避免可变性和装箱,那就没有办法了。 但是你可以创build一个保存原始数组的类,并通过方法提供对元素的只读访问。

虽然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; } } 

去testing:

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

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

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

这个方法返回一个不可变的List ,效率很高。

那么..数组是有用的传递常量(如果他们)作为variables参数。