Java中的不可变数组

Java中的不可变数组,java,arrays,immutability,Java,Arrays,Immutability,在Java中,有没有一种不可变的替代基元数组的方法?制作一个基本数组final实际上并不能阻止人们做类似的事情 final int[] array = new int[] {0, 1, 2, 3}; array[0] = 42; 我希望数组的元素不可更改。不使用基元数组。您需要使用列表或其他数据结构: List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3)); List items=Col

在Java中,有没有一种不可变的替代基元数组的方法?制作一个基本数组
final
实际上并不能阻止人们做类似的事情

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

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

不使用基元数组。您需要使用列表或其他数据结构:

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

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

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 temp=new ArrayList();
临时加法(整数值(0));
温度相加(整数值为(2));
温度相加(整数值为(3));
温度相加(整数值(4));
List immutable=集合。不可修改列表(临时);

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

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

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

public int[] getFooArray() {
  return fooArray == null ? null : fooArray.clone();
}
显然,这是相当昂贵的(因为每次调用getter时都会创建一个完整的副本),但是如果您不能更改接口(例如使用
列表
),并且不能冒险让客户机更改您的内部结构,那么这可能是必要的


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

我的建议是不要使用数组或
不可修改列表,而是使用的,它是为此目的而存在的

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

如果您需要(出于性能原因或为了节省内存)本机“int”而不是“java.lang.Integer”,那么您可能需要编写自己的包装器类。网络上有各种IntArray实现,但没有一个(我发现)是不变的:。可能还有其他的。

嗯。。数组作为常量(如果是)作为变量参数传递是很有用的。

在Java中,有一种方法可以生成不可变数组:

final String[] IMMUTABLE = new String[0];
具有0个元素的数组(显然)不能进行变异

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

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
final static String[]EMPTY_String_ARRAY=新字符串[0];
List emptyList=new ArrayList();
返回emptyList.toArray(空字符串数组);//返回空字符串数组
另一个答案

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"});
}
静态类不可变数组{
私有final T[]数组;
私有不可变数组(T[]a){
array=Arrays.copyOf(a,a.length);
}
来自(T[]a)的公共静态不可变数组{
返回新的ImmutableArray(a);
}
公共T获取(整数索引){
返回数组[索引];
}
}
{
最终ImmutableArray示例=ImmutableArray.from(新字符串[]{“a”、“b”、“c”});
}

在某些情况下,使用Google Guava库中的这种静态方法会更轻量级:
列出Ints.asList(int…backingArray)

示例:

  • List x1=Ints.asList(0,1,2,3)
  • List x1=Ints.asList(新的int[]{0,1,2,3})

如果你想避免易变性和装箱,那就没有出路了。但是您可以创建一个类,该类在内部保存基元数组,并通过方法提供对元素的只读访问。

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

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

自Guava 22以来,从package
com.google.common.primitives
你可以使用三个新类,它们比
不可变列表
具有更低的内存占用

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
他们也有一个建设者。例如:

int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
  .add(1L)
  .add(2L)
  .build();
或者,如果大小在编译时已知:

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

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

从Java 9开始,您可以使用(…)的列表。

此方法返回一个不可变的
列表
,非常有效。

Java9中的方法可以用于创建不可变列表,只需一行:

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

无需编写所有这些
valueOf()
s,自动装箱将解决这些问题。还有
Arrays.asList(0,2,3,4)
将更加简洁。@Joachim:使用
valueOf()
的目的是利用内部整数对象缓存来减少内存消耗/回收。@Esko:阅读自动装箱规范。它做的事情完全相同,所以这里没有区别。@John你永远也不会得到一个NPE把
int
转换成
Integer
;只是在另一方面要小心。@John:你说得对,这可能很危险。但是与其完全避免它,不如理解危险并避免它。他在哪里提到他需要一个getter?@Erik:他没有,但这是不可变数据结构的一个非常常见的用例(我对答案进行了修改,以引用一般的方法,因为该解决方案适用于任何地方,即使它在getter中更常见)。在这里使用
clone()
Arrays.copy()
更好吗?据我所知,根据Joshua Bloch的“有效Java”,clone()肯定是首选方法。第13项的最后一句,“明智地覆盖克隆