Tableau immuable en Java
Existe-t-il une alternative immuable aux tableaux primitifs en Java? Faire un tableau primitif final
n'empêche pas réellement 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 soient immuables.
14 réponses
Pas avec des 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 un unmodifiableList
, mais d'utiliser Goyave's ImmutableList, ce qui existe à cet effet.
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 renvoie un tableau qui n'influence pas le tableau d'origine, vous devez cloner le tableau à chaque fois:
public int[] getFooArray() {
return fooArray == null ? null : fooArray.clone();
}
Évidemment, c'est plutôt cher (car vous allez créer une copie complète chaque fois que vous appelez le getter), mais si vous ne pouvez pas changer l'interface (pour utiliser un List
par exemple) et ne pouvez pas risquer que le client Change vos internes, alors cela peut être nécessaire.
Ce la technique est appelée faire une copie défensive.
Il y a une façon de faire 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 être utile si vous utilisez la List.toArray
méthode pour convertir un List
pour un tableau. Comme même un tableau vide prend 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), il renverra le tableau que vous avez passé, vous permettant de réutiliser ce tableau chaque fois que vous appelez toArray
sur un 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 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 performance ou pour économiser de la mémoire) 'int' natif au lieu de ' java.lang.Integer', alors vous auriez probablement besoin d'écrire votre propre classe wrapper. Il existe différentes implémentations IntArray sur le net, mais aucune (j'ai trouvé) n'était immuable: Koders IntArray, Lucene IntArray . Il y a probablement d'autres.
Depuis Guava 22, à partir du paquet 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 une autre façon d'obtenir une vue immuable d'un tableau pour les primitives Java.
Non, ceci n'est pas possible. Cependant, on pourrait faire quelque chose comme ceci:
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, et est une Liste, pas un tableau, mais est le plus proche que vous obtiendrez.
Dans certaines situations, il sera plus léger d'utiliser cette méthode statique 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 aucun moyen de sortir de la boîte. Mais vous pouvez créer une classe qui contient un tableau primitif à l'intérieur et fournit un accès en lecture seule aux éléments via la ou les méthodes.
Bien qu'il soit vrai que Collections.unmodifiableList()
fonctionne, vous pouvez parfois avoir 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 réellement 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;
}
}
Pour 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 lié.
Le de(E... elements) méthode dans Java9 peut être utilisé pour créer une liste immuable en utilisant juste 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 l'ajout d'un entier à cette liste entraînerait une exception java.lang.UnsupportedOperationException
. Cette méthode accepte un seul tableau comme argument.
String[] array = ... ;
List<String[]> list = List.<String[]>of(array);
Eh bien.. les tableaux sont utiles pour passer en tant que constantes (si elles l'étaient) en tant que paramètres de variantes.