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.

129
demandé sur Jason S 2010-09-13 17:43:12

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));
139
répondu Jason S 2010-09-13 13:45:32

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);
68
répondu ColinD 2016-10-18 07:07:51

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.

19
répondu Joachim Sauer 2010-09-13 14:55:09

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
13
répondu Brigham 2012-08-14 20:51:35

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"});
}
5
répondu aeracode 2017-11-24 17:38:51

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.

3
répondu Thomas Mueller 2010-09-13 14:49:24

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.

2
répondu JeanValjean 2017-08-11 14:54:57

Comme de Java 9, vous pouvez utiliser List.of(...), JavaDoc.

Cette méthode renvoie un List immuable et est très efficace.

2
répondu rmuller 2017-10-25 05:32:38

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.

1
répondu 2010-09-13 13:46:32

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})
1
répondu kevinarpe 2013-07-25 18:38:04

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.

1
répondu Sarge Borsch 2013-07-25 18:41:12

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é.

0
répondu miguelt 2014-07-22 13:30:57

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);
0
répondu i_am_zero 2017-12-14 15:51:16

Eh bien.. les tableaux sont utiles pour passer en tant que constantes (si elles l'étaient) en tant que paramètres de variantes.

-3
répondu Martin 2011-02-18 10:24:58