tutorial example español array java arraylist collections return-type

example - list string java



¿Por qué la enumeración se convierte a ArrayList y no a List en java.utils? (4)

¿Hay una buena razón por la que el método Collections.list() en el paquete java.utils devuelve una ArrayList<T> lugar de List<T> ?

Obviamente, una ArrayList es una List , pero tengo la impresión de que generalmente es una buena práctica devolver el tipo de interfaz en lugar del tipo de implementación.


Al regresar a List , promocionará el programa a una interfaz , lo cual es una muy buena práctica. Sin embargo, este enfoque tiene su limitación. Por ejemplo, no puede usar algunos métodos que están definidos para ArrayList y que no existen en la interfaz de List . Consulte esta respuesta para obtener más detalles.

Cito el diseño de API de los Tutoriales de Java ™:

... Está bien devolver un objeto de cualquier tipo que implemente o extienda una de las interfaces de colección. Esta puede ser una de las interfaces o un tipo de propósito especial que extiende o implementa una de estas interfaces.

.. En cierto sentido, los valores de retorno deben tener el comportamiento opuesto de los parámetros de entrada: es mejor devolver la interfaz de recopilación aplicable más específica en lugar de la más general . Por ejemplo, si está seguro de que siempre devolverá un SortedMap , debe dar al método correspondiente el tipo de retorno de SortedMap lugar de Map . SortedMap instancias de SortedMap más tiempo para crear que las instancias de Map normales y también son más potentes. Dado que su módulo ya ha invertido el tiempo para construir un SortedMap , tiene sentido darle al usuario acceso a su mayor potencia. Además, el usuario podrá pasar el objeto devuelto a métodos que exijan un SortedMap , así como a aquellos que acepten cualquier Map .

Dado que ArrayList es esencialmente una matriz, son mi primera opción cuando necesito tener una "colección-matriz". Entonces, si quiero convertir la enumeración en una lista, mi elección sería una lista de matriz.

En cualquier otro caso, todavía es válido escribir:

List<T> list = Collections.list(e);


Hay una pequeña sobrecarga en los métodos de llamada de una interfaz en lugar de directamente en un objeto.

Esta sobrecarga no suele ser más de 1 o 2 instrucciones del procesador. La sobrecarga de llamar a un método es aún menor si el JIT sabe que el método es final. Esto no es medible para la mayoría de los códigos correctos para usted y para mí, pero para los métodos de bajo nivel en java.utils puede usarse en algunos códigos donde es un problema.

Además, como se ha señalado en otras respuestas, el tipo concreto del objeto que se devuelve (incluso cuando está oculto detrás de una interfaz) afecta el rendimiento del código que lo utiliza. Este cambio en el rendimiento puede ser muy grande, de tal manera que el software de llamada no funciona.

Claramente, los autores de java.utils no tienen forma de saber qué hace todo el software que llama a Collections.list () con el resultado y no hay forma de volver a probar este software si cambian la implantación de Collections.list (). Por lo tanto, no van a cambiar la implantación de Collections.list () para devolver un tipo diferente de Lista, ¡incluso si el sistema de tipos lo permite!

Al escribir su propio software, usted (con suerte) tiene una prueba automatizada que cubre todo su código y una buena comprensión de cómo se interrelaciona su código incluye saber dónde es un problema el rendimiento. Ser capaz de hacer un cambio en un método, sin tener que cambiar las llamadas, es de gran valor mientras el diseño del software está cambiando.

Por lo tanto, las dos compensaciones son muy diferentes.


Las funciones que devuelven la "propiedad exclusiva" de los objetos mutables recién creados a menudo deberían ser el tipo más específico práctico; aquellos que devuelven objetos inmutables, especialmente si se pueden compartir, a menudo deberían devolver tipos menos específicos.

La razón de la distinción es que en el primer caso, un objeto siempre podrá producir un nuevo objeto del tipo indicado, y dado que el destinatario será el propietario del objeto y no se sabe qué acciones podría desear realizar el destinatario, allí Por lo general, no sería posible que el código que devuelve el objeto sepa si alguna implementación de interfaz alternativa podría satisfacer las necesidades del destinatario.

En el último caso, el hecho de que el objeto es inmutable significa que la función puede identificar un tipo alternativo que puede hacer todo lo que un tipo más complicado podría hacer dado su contenido exacto . Por ejemplo, una interfaz Immutable2dMatrix podría implementarse mediante una clase ImmutableArrayBacked2dMatrix y una clase ImmutableDiagonal2dMatrix . Una función que se supone que devuelve un cuadrado Immutable2dMatrix podría decidir devolver una instancia ImmutableDiagonalMatrix si todos los elementos fuera de la diagonal principal son cero, o un ImmutableArrayBackedMatrix si no. El primer tipo ocuparía mucho menos espacio de almacenamiento, pero al destinatario no debería importarle la diferencia entre ellos.

Devolver Immutable2dMatrix lugar de un ImmutableArrayBackedMatrix concreto permite que el código elija el tipo de retorno en función de lo que contiene la matriz; también significa que si el código que se supone que devuelve la matriz tiene una implementación adecuada de Immutable2dMatrix , simplemente puede devolver eso, en lugar de tener que construir una nueva instancia. Ambos factores pueden ser importantes "victorias" cuando se trabaja con objetos inmutables.

Sin embargo, cuando se trabaja con objetos mutables, ninguno de los factores entra en juego. El hecho de que una matriz mutable no tenga elementos fuera de la diagonal principal cuando se genera no significa que nunca tendrá tales elementos. En consecuencia, mientras que una ImmutableDiagonalMatrix es efectivamente un subtipo de una MutableDiagonalMatrix , una MutableDiagonalMatrix no es un subtipo de una Mutable2dMatrix , ya que la última podría aceptar tiendas fuera de la diagonal principal mientras que la primera no. Además, aunque los objetos inmutables a menudo pueden y deben compartirse, los objetos mutables generalmente no pueden. Una función que solicita una nueva colección mutable inicializada con cierto contenido deberá crear una nueva colección, independientemente de si su almacén de respaldo coincide o no con el tipo solicitado.


Descargo de responsabilidad: no soy un autor de JDK.

Estoy de acuerdo en que es correcto escribir su propio código en las interfaces, pero si va a devolver una colección mutable a un tercero, es importante que el tercero sepa qué tipo de List está recuperando.

LinkedList y ArrayList son muy diferentes, en cuanto al rendimiento, para diversas operaciones. Por ejemplo, eliminar el primer elemento de una ArrayList es O(n) , pero eliminar el primer elemento de una LinkedList es O(1) .

Al especificar completamente el tipo de retorno, los autores de JDK están comunicando información adicional , en código inequívoco, sobre qué tipo de objeto le están devolviendo, para que pueda escribir su código para usar este método correctamente. Si realmente necesita una LinkedList , sabe que debe especificar una aquí.

Finalmente, la razón principal para codificar una interfaz sobre una implementación es si cree que la implementación cambiará. Los autores de JDK probablemente piensan que nunca van a cambiar este método; nunca va a devolver una LinkedList o una Collections.UnmodifiableList . Sin embargo, en la mayoría de los casos, probablemente aún haría:

List<T> list = Collections.list(enumeration);