Immutable array in Java

JavaArraysImmutability

Java Problem Overview


Is there an immutable alternative to the primitive arrays in Java? Making a primitive array final doesn't actually prevent one from doing something like

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

I want the elements of the array to be unchangeable.

Java Solutions


Solution 1 - Java

Not with primitive arrays. You'll need to use a List or some other data structure:

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

Solution 2 - Java

My recommendation is to not use an array or an unmodifiableList but to use Guava's ImmutableList, which exists for this purpose.

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

Solution 3 - Java

As others have noted, you can't have immutable arrays in Java.

If you absolutely need a method that returns an array that doesn't influence the original array, then you'd need to clone the array each time:

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

Obviously this is rather expensive (as you'll create a full copy each time you call the getter), but if you can't change the interface (to use a List for example) and can't risk the client changing your internals, then it may be necessary.

This technique is called making a defensive copy.

Solution 4 - Java

There is one way to make an immutable array in Java:

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

Arrays with 0 elements (obviously) cannot be mutated.

This can actually come in handy if you are using the List.toArray method to convert a List to an array. Since even an empty array takes up some memory, you can save that memory allocation by creating a constant empty array, and always passing it to the toArray method. That method will allocate a new array if the array you pass doesn't have enough space, but if it does (the list is empty), it will return the array you passed, allowing you to reuse that array any time you call toArray on an empty 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

Solution 5 - Java

As of Java 9 you can use List.of(...), JavaDoc.

This method returns an immutable List and is very efficient.

Solution 6 - Java

Another one answer

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

Solution 7 - Java

Since Guava 22, from package com.google.common.primitives you can use three new classes, which have a lower memory footprint compared to ImmutableList.

They also have a builder. Example:

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

or, if the size is known at compile-time:

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

This is another way of getting an immutable view of an array for Java primitives.

Solution 8 - Java

If you need (for performance reason or to save memory) native 'int' instead of 'java.lang.Integer', then you would probably need to write your own wrapper class. There are various IntArray implementations on the net, but none (I found) was immutable: Koders IntArray, Lucene IntArray. There are probably others.

Solution 9 - Java

The of(E... elements) method in Java9 can be used to create immutable list using just a line:

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

The above method returns an immutable list containing an arbitrary number of elements. And adding any integer to this list would result in java.lang.UnsupportedOperationExceptionexception. This method also accepts a single array as an argument.

String[] array = ... ;
List<String[]> list = List.<String[]>of(array);

Solution 10 - Java

No, this is not possible. However, one could do something like this:

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

This requires using wrappers, and is a List, not an array, but is the closest you will get.

Solution 11 - Java

In some situations, it will be lighter weight to use this static method from Google Guava library: List<Integer> Ints.asList(int... backingArray)

Examples:

  • List<Integer> x1 = Ints.asList(0, 1, 2, 3)
  • List<Integer> x1 = Ints.asList(new int[] { 0, 1, 2, 3})

Solution 12 - Java

If you want to avoid both mutability and boxing, there is no way out of the box. But you can create a class which holds primitive array inside and provides read-only access to elements via method(s).

Solution 13 - Java

While it's true that Collections.unmodifiableList() works, sometimes you may have a large library having methods already defined to return arrays (e.g. String[]). To prevent breaking them, you can actually define auxiliary arrays that will store the values:

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

To test:

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

Not perfect, but at least you have "pseudo immutable arrays" (from the class perspective) and this will not break related code.

Solution 14 - Java

Implement java.util.function.IntUnaryOperator:

class ImmutableArray implements IntUnaryOperator {
  private final int[] array;
  ImmutableArray(int[] array) {
     this.array = Arrays.copyOf(array, array.length);
  }
  @Override
    public int applyAsInt(int index) {
    return array[index];
  }
}

Access the array: array[i] becomes immutableArray.applyAsInt(i).

  • I benchmarked primitive for loop retrieval with a modulus operation with 100_000_000 elements. The above PrimitiveArray took ~220ms; there was no significant difference with a primitive array. The same op on ArrayList took 480 ms, and the loading process took 21 seconds, depleted my heap space first try, and I had to increase this setting on the JVM. Loading of PrimitiveArray had taken 2 seconds.

iteration

  • if you want to iterate, implement Iterable and provide

    public java.util.PrimitiveIterator.OfInt iterator() { return Arrays.stream(array).iterator(); }

    This provides access to int nextInt method.

  • From PrimitiveIterator you also get method forEachRemaining(PrimitiveConsumer) which is helpful to replace an existing enhanced for loop.

  • Iterating manually with PrimitiveIterator.OfInt yielded performance of ~300ms.

Solution 15 - Java

Well.. arrays are useful to pass as constants (if they were) as variants parameters.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionignoramusView Question on Stackoverflow
Solution 1 - JavaJason SView Answer on Stackoverflow
Solution 2 - JavaColinDView Answer on Stackoverflow
Solution 3 - JavaJoachim SauerView Answer on Stackoverflow
Solution 4 - JavaBrighamView Answer on Stackoverflow
Solution 5 - JavarmullerView Answer on Stackoverflow
Solution 6 - JavaaeracodeView Answer on Stackoverflow
Solution 7 - JavaJeanValjeanView Answer on Stackoverflow
Solution 8 - JavaThomas MuellerView Answer on Stackoverflow
Solution 9 - Javaakhil_mittalView Answer on Stackoverflow
Solution 10 - Javauser439793View Answer on Stackoverflow
Solution 11 - JavakevinarpeView Answer on Stackoverflow
Solution 12 - JavaDisplay NameView Answer on Stackoverflow
Solution 13 - JavamigueltView Answer on Stackoverflow
Solution 14 - JavaJohnView Answer on Stackoverflow
Solution 15 - JavaMartinView Answer on Stackoverflow