有没有Java原始数组的不变选择?制作一个原始数组final
实际上并不能阻止人们做类似的事情
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
我希望数组的元素不可更改。
int[]
,new int[]
并且Much比List<Integer>
and 更容易键入new ArrayList<Integer>
?XD
有没有Java原始数组的不变选择?制作一个原始数组final
实际上并不能阻止人们做类似的事情
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
我希望数组的元素不可更改。
int[]
,new int[]
并且Much比List<Integer>
and 更容易键入new ArrayList<Integer>
?XD
Answers:
不适用于原始数组。您将需要使用列表或其他数据结构:
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
我的建议是不要使用数组或an,unmodifiableList
而要使用为此目的而存在的Guava的ImmutableList。
ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
Collections.unmodifiableList
不足以使List不可变?
正如其他人指出的那样,Java中不能有不可变的数组。
如果您绝对需要一个返回不影响原始数组的数组的方法,则您需要每次都克隆该数组:
public int[] getFooArray() {
return fooArray == null ? null : fooArray.clone();
}
显然,这是相当昂贵的(因为每次调用getter时都会创建一个完整副本),但是如果您不能更改接口(List
例如使用a )并且不能冒险让客户更改内部结构,那么可能有必要。
这种技术称为制作防御性副本。
clone()
还是Arrays.copy()
在这里更好?
有一种方法可以在Java中创建不可变数组:
final String[] IMMUTABLE = new String[0];
具有0个元素的数组(显然)不能被突变。
如果您正在使用List.toArray
方法将a转换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"});
}
如果您需要(出于性能原因或为了节省内存)本机“ int”而不是“ java.lang.Integer”,则可能需要编写自己的包装器类。网上有各种各样的IntArray实现,但是(我发现)没有一成不变的:Koders IntArray,Lucene IntArray。可能还有其他人。
从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原语数组不变视图的另一种方法。
不,这是不可能的。但是,可以执行以下操作:
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的内部。