Existe-t-il une alternative immuable aux tableaux primitifs en Java? Faire un tableau primitif final
n'empêche pas en fait de faire quelque chose comme
final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;
Je veux que les éléments du tableau ne soient pas modifiables.
Pas avec les tableaux primitifs. Vous devrez utiliser une liste ou une autre structure de données:
List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
Ma recommandation est de ne pas utiliser un tableau ou une unmodifiableList
mais d'utiliser Guava 's ImmutableList , qui existe à cette fin.
ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
Comme d'autres l'ont noté, vous ne pouvez pas avoir de tableaux immuables en Java.
Si vous avez absolument besoin d'une méthode qui retourne un tableau qui n'influence pas le tableau d'origine, vous devez alors cloner le tableau à chaque fois:
public int[] getFooArray() {
return fooArray == null ? null : fooArray.clone();
}
Évidemment, cela coûte assez cher (vous allez créer une copie complète chaque fois que vous appelez le getter), mais si vous ne pouvez pas modifier l’interface (pour utiliser un List
par exemple) et ne pouvez pas risquer que le client modifie vos données internes, alors il peut être nécessaire.
Cette technique s'appelle faire une copie défensive.
Il existe un moyen de créer un tableau immuable en Java:
final String[] IMMUTABLE = new String[0];
Les tableaux avec 0 éléments (évidemment) ne peuvent pas être mutés.
Cela peut s'avérer utile si vous utilisez la méthode List.toArray
pour convertir un List
en un tableau. Étant donné que même un tableau vide utilise de la mémoire, vous pouvez enregistrer cette allocation de mémoire en créant un tableau vide constant et en le passant toujours à la méthode toArray
. Cette méthode allouera un nouveau tableau si le tableau que vous passez n'a pas assez d'espace, mais si c'est le cas (la liste est vide), elle retournera le tableau que vous avez passé, ce qui vous permettra de le réutiliser chaque fois que vous appelez toArray
sur une List
vide.
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
Une autre une réponse
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"});
}
Si vous avez besoin (pour des raisons de performances ou pour économiser de la mémoire) natif 'int' au lieu de 'Java.lang.Integer', vous devrez probablement écrire votre propre classe de wrapper. Il existe différentes implémentations d'IntArray sur le réseau, mais aucune (j'ai trouvé) n'était immuable: Koders IntArray , Lucene IntArray . Il y en a probablement d'autres.
A partir de Java 9, vous pouvez utiliser List.of(...)
, JavaDoc .
Cette méthode retourne une List
immuable et est très efficace.
Depuis Guava 22, à partir du package com.google.common.primitives
, vous pouvez utiliser trois nouvelles classes, qui ont une empreinte mémoire inférieure à ImmutableList
.
Ils ont aussi un constructeur. Exemple:
int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
.add(1L)
.add(2L)
.build();
ou, si la taille est connue au moment de la compilation:
ImmutableLongArray longArray = ImmutableLongArray.of(1L, 2L);
C'est un autre moyen d'obtenir une vue immuable d'un tableau pour les primitives Java.
Non, ce n'est pas possible. Cependant, on pourrait faire quelque chose comme ça:
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);
Cela nécessite l'utilisation de wrappers. Il s'agit d'une liste, pas d'un tableau, mais du plus proche possible.
Dans certaines situations, il sera plus léger d’utiliser cette méthode statique à partir de la bibliothèque Google Guava: List<Integer> Ints.asList(int... backingArray)
Exemples:
List<Integer> x1 = Ints.asList(0, 1, 2, 3)
List<Integer> x1 = Ints.asList(new int[] { 0, 1, 2, 3})
Si vous voulez éviter à la fois la mutabilité et la boxe, il n'y a pas moyen de sortir de la boîte. Mais vous pouvez créer une classe contenant un tableau primitif et fournissant un accès en lecture seule aux éléments via une ou plusieurs méthodes.
La méthode de (éléments E ...) dans Java9 peut être utilisée pour créer une liste immuable en utilisant simplement une ligne:
List<Integer> items = List.of(1,2,3,4,5);
La méthode ci-dessus renvoie une liste immuable contenant un nombre arbitraire d'éléments. Et ajouter un entier à cette liste entraînerait une exception Java.lang.UnsupportedOperationException
exception. Cette méthode accepte également un seul tableau en tant qu'argument.
String[] array = ... ;
List<String[]> list = List.<String[]>of(array);
S'il est vrai que Collections.unmodifiableList()
fonctionne, vous pouvez parfois disposer d'une grande bibliothèque ayant des méthodes déjà définies pour renvoyer des tableaux (par exemple, String[]
) . Pour éviter de les casser, vous pouvez définir des tableaux auxiliaires qui stockeront les valeurs:
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;
}
}
Tester:
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()));
Pas parfait, mais au moins vous avez des "tableaux pseudo immuables" (du point de vue de la classe) et cela ne cassera pas le code associé.