java design-patterns immutable-collections

java - ¿Es mejor devolver un ImmutableMap o un Mapa?



design-patterns immutable-collections (9)

Digamos que estoy escribiendo un método que debería devolver un Map . Por ejemplo:

public Map<String, Integer> foo() { return new HashMap<String, Integer>(); }

Después de pensarlo por un tiempo, he decidido que no hay razón para modificar este Mapa una vez que se crea. Por lo tanto, me gustaría devolver un ImmutableMap .

public Map<String, Integer> foo() { return ImmutableMap.of(); }

¿Debo dejar el tipo de retorno como un Mapa genérico, o debo especificar que estoy devolviendo un ImmutableMap?

Por un lado, esto es exactamente por qué se crearon las interfaces; para ocultar los detalles de implementación.
Por otro lado, si lo dejo así, otros desarrolladores podrían perderse el hecho de que este objeto es inmutable. Por lo tanto, no lograré un objetivo principal de objetos inmutables; para que el código sea más claro minimizando la cantidad de objetos que pueden cambiar. Peor aún, después de un tiempo, alguien podría intentar cambiar este objeto, y esto provocará un error de tiempo de ejecución (el compilador no lo advertirá).


Por otro lado, si lo dejo así, otros desarrolladores podrían perderse el hecho de que este objeto es inmutable.

Deberías mencionar eso en los javadocs. Los desarrolladores los leen, ya sabes.

Por lo tanto, no lograré un objetivo principal de objetos inmutables; para que el código sea más claro minimizando la cantidad de objetos que pueden cambiar. Peor aún, después de un tiempo, alguien podría intentar cambiar este objeto, y esto provocará un error de tiempo de ejecución (el compilador no lo advertirá).

Ningún desarrollador publica su código sin probar. Y cuando lo prueba, obtiene una excepción donde no solo ve la razón, sino también el archivo y la línea donde intentó escribir en un mapa inmutable.

Sin embargo, tenga en cuenta que solo el Map sí será inmutable, no los objetos que contiene.


si lo dejo así, otros desarrolladores podrían perderse el hecho de que este objeto es inmutable

Eso es cierto, pero otros desarrolladores deben probar su código y asegurarse de que esté cubierto.

Sin embargo, tienes 2 opciones más para resolver esto:

  • Use Javadoc

    @return a immutable map

  • Elija un nombre de método descriptivo

    public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries()

    Para un caso de uso concreto, incluso puede nombrar mejor los métodos. P.ej

    public Map<String, Integer> getUnmodifiableCountByWords()

¡¿Qué más puedes hacer?!

Puedes devolver un

  • Copiar

    private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); }

    Este enfoque se debe utilizar si espera que muchos clientes modifiquen el mapa y siempre que el mapa solo contenga algunas entradas.

  • CopyOnWriteMap

    Copiar en las colecciones de escritura generalmente se usa cuando tiene que lidiar con
    concurrencia Pero el concepto también lo ayudará en su situación, ya que un CopyOnWriteMap crea una copia de la estructura de datos interna en una operación mutativa (por ejemplo, agregar, eliminar).

    En este caso, necesita un envoltorio delgado alrededor de su mapa que delegue todas las invocaciones de métodos al mapa subyacente, excepto las operaciones mutativas. Si se invoca una operación mutativa, crea una copia del mapa subyacente y todas las invocaciones adicionales se delegarán a esta copia.

    Este enfoque se debe usar si espera que algunos clientes modifiquen el mapa.

    Lamentablemente, Java no tiene un CopyOnWriteMap . Pero puede encontrar un tercero o implementarlo usted mismo.

Por último, debe tener en cuenta que los elementos en su mapa aún pueden ser mutables.


Debe tener ImmutableMap como su tipo de retorno. Map contiene métodos que no son compatibles con la implementación de ImmutableMap (por ejemplo, put ) y están marcados como @deprecated en @deprecated .

El uso de métodos obsoletos dará como resultado una advertencia del compilador y la mayoría de los IDE avisarán cuando las personas intenten utilizar los métodos obsoletos.

Esta advertencia avanzada es preferible a tener excepciones de tiempo de ejecución como su primer indicio de que algo está mal.


Definitivamente devuelve un ImmutableMap, la justificación es:

  • La firma del método (incluido el tipo de retorno) debe autodocumentarse. Los comentarios son como el servicio al cliente: si sus clientes necesitan confiar en ellos, entonces su producto principal es defectuoso.
  • Si algo es una interfaz o una clase solo es relevante al extenderlo o implementarlo. Dada una instancia (objeto), el 99% del tiempo el código del cliente no sabrá ni le importará si algo es una interfaz o una clase. Al principio supuse que ImmutableMap era una interfaz. Solo después de hacer clic en el enlace me di cuenta de que es una clase.

Depende de la clase misma. ImmutableMap de Guava no pretende ser una vista inmutable en una clase mutable. Si su clase es inmutable y tiene alguna estructura que es básicamente un ImmutableMap , entonces haga el tipo de retorno ImmutableMap . Sin embargo, si tu clase es mutable, no lo hagas. Si tienes esto:

public ImmutableMap<String, Integer> foo() { return ImmutableMap.copyOf(internalMap); }

Guayaba copiará el mapa cada vez. Eso es lento Pero si internalMap ya era un ImmutableMap , entonces está totalmente bien.

Si no restringe su clase para que devuelva ImmutableMap , podría devolver Collections.unmodifiableMap manera:

public Map<String, Integer> foo() { return Collections.unmodifiableMap(internalMap); }

Tenga en cuenta que esta es una vista inmutable en el mapa. Si internalMap cambia, también lo hará una copia en caché de Collections.unmodifiableMap(internalMap) . Sin embargo, todavía lo prefiero para los captadores.


El mapa inmutable es un tipo de mapa. Entonces, dejar el tipo de retorno de Mapa está bien.

Para garantizar que los usuarios no modifiquen el objeto devuelto, la documentación del método puede describir las características del objeto devuelto.


Esto es posiblemente una cuestión de opinión, pero la mejor idea aquí es utilizar una interfaz para la clase de mapa. Esta interfaz no necesita decir explícitamente que es inmutable, pero el mensaje será el mismo si no expone ninguno de los métodos de establecimiento de la clase padre en la interfaz.

Echa un vistazo al siguiente artículo:

Andy Gibson


Esto no responde a la pregunta exacta, pero vale la pena considerar si un mapa debe ser devuelto. Si el mapa es inmutable, entonces el método principal que se debe proporcionar se basa en la clave get (clave):

public Integer fooOf(String key) { return map.get(key); }

Esto hace que la API sea mucho más estricta. Si realmente se requiere un mapa, esto podría dejarse en manos del cliente de la API al proporcionar una secuencia de entradas:

public Stream<Map.Entry<String, Integer>> foos() { map.entrySet().stream() }

Luego, el cliente puede hacer su propio mapa inmutable o mutable según lo necesite, o agregar las entradas a su propio mapa. Si el cliente necesita saber si el valor existe, se puede devolver opcional en su lugar:

public Optional<Integer> fooOf(String key) { return Optional.ofNullable(map.get(key)); }


  • Si está escribiendo una API pública y esa inmutabilidad es un aspecto importante de su diseño, definitivamente lo haría explícito si el nombre del método denota claramente que el mapa devuelto será inmutable o al devolver el tipo concreto de el mapa. Mencionarlo en el javadoc no es suficiente en mi opinión.

    Como aparentemente estás usando la implementación de Guava, miré el documento y es una clase abstracta, por lo que te da un poco de flexibilidad en el tipo concreto real.

  • Si está escribiendo una herramienta / biblioteca interna, se vuelve mucho más aceptable simplemente devolver un Map simple. Las personas conocerán las partes internas del código al que están llamando o al menos tendrán fácil acceso a él.

Mi conclusión sería que explícito es bueno, no deje las cosas al azar.