2010-09-13 24 views
121

¿Existe una alternativa inmutable a las matrices primitivas en Java? Haciendo una matriz primitiva final no impide en realidad que uno haga algo así comomatriz inmutable en Java

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

Quiero que los elementos de la matriz para ser inmutable.

+42

Hágase un favor y parada utilizando matrices de Java para nada, además de 1) io 2) Número pesado crujido 3) si es necesario poner en práctica su propia lista/Collection (que es raro * *). Son extremadamente inflexibles y anticuados ... como acabas de descubrir con esta pregunta. – whaley

+31

@Whaley, y actuaciones, y código en el que no necesita arreglos "dinámicos". Las matrices siguen siendo útiles en muchos lugares, no es tan raro. –

+10

@ Colin: sí, pero son severamente restrictivas; lo mejor es acostumbrarse a pensar "¿realmente necesito una matriz aquí o puedo usar una lista?" –

Respuesta

132

No con matrices primitivas. Tendrá que utilizar una lista o alguna otra estructura de datos:

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

De alguna manera, nunca supe acerca de Arrays.asList (T ...). Supongo que puedo deshacerme de mi ListUtils.list (T ...) ahora. – MattRS

+1

Por cierto, Arrays.asList proporciona una lista no modificable – mauhiz

+2

@mauhiz, no, no según la fuente, Arrays.asList (matriz) devuelve una nueva ArrayList <> (matriz), que tiene métodos de configuración perfectamente válidos ... –

1

No, esto no es posible. Sin embargo, se podría hacer algo como esto:

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

Esto requiere el uso de envolturas, y es una lista, no una matriz, pero es el más cercano que usted conseguirá.

+4

No es necesario escribir todos los 'valueOf()' s, el autoboxing se encargará de eso. También 'Arrays.asList (0, 2, 3, 4)' sería mucho más conciso. –

+0

@Joachim: El punto de uso de 'valueOf()' es utilizar el caché de objetos Integer interno para reducir el consumo/reciclaje de memoria. – Esko

+4

@Esko: lea las especificaciones de autoboxing. Hace exactamente lo mismo, por lo que no hay diferencia aquí. –

17

Como han notado otros, no puede tener matrices inmutables en Java.

Si es absolutamente necesario un método que devuelve una matriz que no influye en la matriz original, a continuación, que había necesidad de clonar la matriz cada vez:

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

Obviamente esto es bastante caro (como se Creará una copia completa cada vez que llame al captador), pero si no puede cambiar la interfaz (para usar un List por ejemplo) y no puede arriesgarse a que el cliente cambie sus partes internas, entonces puede ser necesario.

Esta técnica se llama hacer una copia defensiva.

+3

¿Dónde mencionó que necesitaba un getter? –

+5

@Erik: no fue así, pero ese es un caso de uso muy común para las estructuras de datos inmutables (modifiqué la respuesta para referirme a los métodos en general, ya que la solución se aplica en todas partes, incluso si es más común en getters). –

+4

¿Es mejor usar 'clone()' o 'Arrays.copy()' aquí? – kevinarpe

65

Mi recomendación es no utilizar una matriz o un unmodifiableList, pero para usar el GuavaImmutableList, que existe para este propósito.

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

+1 Guava ImmutableList es incluso mejor que Collections.unmodifiableList, porque es un tipo separado. – sleske

+26

ImmutableList es * a menudo * mejor (depende del caso de uso) porque es inmutable. Collections.unmodifiableList no es inmutable. Más bien, es una visión de que los receptores no pueden cambiar, pero la fuente original PUEDE cambiar. –

+2

@CharlieCollins, si no hay forma de acceder a la fuente original de lo que es 'Collections.unmodifiableList' suficiente para hacer una Lista inmutable? – savanibharat

3

Si necesita (por motivos de rendimiento o para ahorrar memoria) 'int' nativo en lugar de 'java.lang.Integer', entonces es probable que tenga que escribir su propia clase contenedora. Hay varias implementaciones de IntArray en la red, pero ninguna (encontré) era inmutable: Koders IntArray, Lucene IntArray. Probablemente haya otros.

-3

Bueno ... las matrices son útiles para pasar como constantes (si lo fueran) como parámetros de variantes.

+0

¿Qué es un "parámetro de variantes"? Nunca lo oí. – sleske

+0

Lo sentimos, varargs;) – Martin

+2

El uso de arreglos como constantes es * exactamente * donde muchas personas FALLAN. La referencia es constante, pero el contenido de la matriz es mutable. Una persona que llama/cliente podría cambiar el contenido de su "constante". Probablemente esto no sea algo que quieras permitir. Use una ImmutableList. –

11

Hay una manera de hacer una serie inmutable en Java:

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

matrices con elementos 0 (obviamente) no se puede mutar.

Esto realmente puede ser útil si está utilizando el método List.toArray para convertir un List en una matriz. Dado que incluso una matriz vacía ocupa algo de memoria, puede guardar esa asignación de memoria creando una matriz vacía constante, y siempre pasándola al método toArray. Ese método asignará una nueva matriz si la matriz que pasa no tiene suficiente espacio, pero si lo hace (la lista está vacía), devolverá la matriz que pasó, lo que le permite reutilizar dicha matriz cada vez que llame al toArray en un List vacío.

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 
+1

Un buen punto acerca de las matrices vacías puede ser inmutable. – kevinarpe

+1

Pero esto no ayuda al OP a lograr una matriz inmutable con datos en ella. –

+1

@ Sridhar-Sarnobat Ese es el punto de la respuesta. No hay forma en Java de crear una matriz inmutable con datos en ella. – Brigham

4

Otra respuesta

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

¿De qué sirve copiar esa matriz en el constructor, ya que no proporcionamos ningún método setter? –

+0

Un contenido de la matriz de entrada aún se puede modificar posteriormente. Queremos preservar su estado original, entonces lo copiamos. – aeracode

1

En algunas situaciones, será un peso más ligero para utilizar este método estático de la biblioteca de Google Guava: List<Integer> Ints.asList(int... backingArray)

Ejemplos:

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

Si desea evitar la mutabilidad y el boxeo, no hay forma de salir de la caja. Pero puede crear una clase que contenga una matriz primitiva dentro y que proporcione acceso de solo lectura a los elementos a través de los métodos.

0

Si bien es cierto que Collections.unmodifiableList() funciona, a veces puede tener una gran biblioteca con métodos ya definidos para devolver matrices (por ejemplo, String[]). Para evitar romperlos, en realidad se puede definir matrices auxiliares que almacenarán los valores:

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

Para probar:

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

No es perfecto, pero al menos tienen "pseudo matrices inmutables" (de la perspectiva de clase) y esto no romperá el código relacionado.

2

Desde Guava 22, desde el paquete com.google.common.primitives puede utilizar tres nuevas clases, que tienen una memoria menor en comparación con ImmutableList.

También tienen un constructor. Ejemplo:

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

o, si el tamaño es conocido en tiempo de compilación:

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

Esta es otra manera de conseguir una vista inmutable de una matriz para las primitivas de Java.

1

A partir de Java 9 puede usar List.of(...), JavaDoc.

Este método devuelve un List inmutable y es muy eficiente.

0

El método of(E... elements) en Java9 puede ser usado para crear lista inmutable usar sólo una línea:

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

El método anterior devuelve una lista inmutable que contiene un número arbitrario de elementos.Y agregar cualquier número entero a esta lista daría como resultado la excepción java.lang.UnsupportedOperationException. Este método también acepta una única matriz como argumento.

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