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参数。