2009-06-26 8 views
17

Estoy intentando comprender la incoherencia de la API en la biblioteca de colecciones estándar de Java.¿Por qué la interfaz de la Lista de Java no es compatible con getLast()?

No hay ningún método en List o en AbstractList para obtener el último elemento, aunque se puede simular eso con size y getIndex().

Sin embargo, LinkedList admite esa función.

¿Alguna idea de por qué se decidió no admitir este método en la interfaz?

+0

Después de tratarlo varias veces en el código, estoy bastante seguro de que es así solo para que los programadores de mantenimiento se enojen con el diseñador de la API. :-) Los programadores de Greenfield terminan pasando LinkedList (en lugar de List) para que puedan usar getLast().Los chicos de mantenimiento descubren que ArrayList es más apropiado y tienen que lidiar con todas estas LinkedLists especificadas que realmente deberían haber sido List en primer lugar (y probablemente lo hubieran sido a excepción de esta crítica falla en la interfaz List) ... –

Respuesta

19

La interfaz java.util.List no es compatible con getLast() porque los diseñadores optaron por una "interfaz mínima". Con la cantidad mínima de métodos definidos, hace que sea más fácil de entender y más rápido de aprender.

Esto está en contraste con una 'interfaz humana' (como la utilizada en el Ruby array class) que intenta proporcionar métodos para realizar operaciones comunes (por ejemplo, getLast()). Como hay muchos usos que un concepto tan fundamental como una lista se puede poner a esto tiende a conducir a interfaces mucho más grandes.

Para obtener más información, consulte las descripciones de Martin Fowler Minimal Interface y Humane Interface.

cuanto a por qué ListaEnlazada apoya getLast() etc., por citar el javadoc:

... la clase LinkedList proporciona uniformemente nombrado métodos para obtener, extraer e insertar un elemento al principio y al final de la lista .Estas operaciones permiten que las listas vinculadas se usen como una pila, una cola o una cola de doble extremo (deque).

Presumiblemente se pensó que una Lista general no sería adecuada para estos casos de uso específicos.

Como una idea de la mente del principal diseñador de la API de colecciones de Java (Joshua Bloch) proporciona this list of API design maxims con el que trabaja. De los cuales, los más pertinentes a esta pregunta son:

Los primeros borradores de las API deben ser cortos, generalmente una página con las firmas de clase y método y las descripciones de una línea. Esto facilita la reestructuración de la API cuando no la haces bien la primera vez.

En caso de duda, déjalo fuera. Si hay un teorema fundamental del diseño de API, esto es todo. Se aplica por igual a la funcionalidad, clases, métodos y parámetros. Cada faceta de una API debe ser lo más pequeña posible, pero no más pequeña. Siempre puede agregar cosas más tarde, pero no puede llevárselas. Minimizar el peso conceptual es más importante que el conteo de clase o método.

Mantenga las API sin detalles de implementaciones. Confunden a los usuarios e inhiben la flexibilidad para evolucionar. No siempre es obvio cuál es un detalle de implementación: tenga cuidado con la sobreespecificación.

Minimiza el acceso; cuando tengas dudas, hazlo en privado. Esto simplifica las API y reduce el acoplamiento.

Tenga en cuenta las consecuencias de rendimiento de las decisiones de diseño de API, pero no deformar una API para lograr mejoras de rendimiento. Afortunadamente, las buenas API generalmente se prestan para implementaciones rápidas.

Sin embargo, también afirma:

No haga el cliente hace nada la biblioteca podría hacer. La violación de esta regla conduce a un código repetitivo en el cliente, que es molesto y propenso a errores.

Lo que acaba de mostrar que las directrices de diseño a menudo entran en conflicto y la parte más difícil de un trabajo de diseñadores de API es equilibrar estos conflictos.

+8

Buena explicación. Me hace preguntarme por qué hay un método isEmpty sin embargo. – peskal

+0

@peskal Con Java 8 podemos usar isEmpty como referencia de método, lo cual es bueno. –

+0

@ ThorbjørnRavnAndersen pero existía mucho antes que Java 8;) – Joffrey

2

El objetivo de una interfaz es proporcionar la máxima capacidad de uso con la menor cantidad posible de métodos públicos. Cuantos menos métodos hay para apoyar, mejor. No hay método getLast() porque esto se puede derivar, como dijiste.

LinkedList, por otro lado es una implementación concreta, y por lo tanto no tiene estos problemas.

Editar: como Skaffman señaló, este no es el objetivo principal de una interfaz. Es más un objetivo secundario que permite que las implementaciones de esta interfaz sean más fáciles de mantener. El objetivo principal de una interfaz es desacoplar la implementación concreta de los objetos que la utilizan.

+8

No , el objetivo de una interfaz es separar la implementación de la interfaz. El tipo de operaciones no tiene nada que ver con eso. – skaffman

+0

No estoy seguro de estar de acuerdo, pero supongo que depende de la probabilidad de que alguien desee el último elemento de una lista. – Uri

+1

Me pregunto qué otras implementaciones de lista son posibles aparte de las incorporadas en LinkedList/ArrayList en las que un método común de interfaz getFirst() getLast() no hubiera sido O (1)? – akarnokd

5

Por lo general, la razón es que querían especificar cada función con un requisito de Big-O y consideraban que getLast() no se podía implementar de manera eficiente en todas las listas. Entonces lo presentan en cada nivel con su promesa de Big-O.

O podría haber sido un descuido, o sintieron que no era lo suficientemente común y si lo necesitaban, podían obtenerlo con size/getIndex.

+3

A veces me gustaría haberlo agregado también a ArrayList. Escribir arraylist.get (arraylist.size() - 1) una y otra vez es una especie de repetición. – akarnokd

+1

public class MyArrayList extends ArrayList {public E getLast() {return get (size() - 1);}} – Charlie

+2

@Charlie: A menos que trabaje con una interfaz de lista y no quiera saber directamente cuál es su subyacente implementación. – Uri

1

Como dijiste, con List puedes obtener la última con getIndex(), ya que está basada en índices. Personalmente, no veo ninguna buena razón para expresarlo (ya que puedes escribirlo tú mismo).

Mientras que LinkedList no está basado en una matriz, por lo que no hay índices, tiene sentido proporcionar un método como ese, a veces es necesario conocer el último elemento.

0

getLast() no se pudo implementar en una lista de enlaces unidireccionales. getIndex() en una lista de enlaces unidireccionales probablemente escaneará toda la lista desde el principio, por lo que tiene una implicación en la eficiencia.

+1

A menos que solo haya almacenado el último elemento en un campo. ¿Cómo crees que se implementa getSize()? –

+0

Esta es la razón por la que LinkedList de Java tiene un enlace doble o en caso de enlace único, la lista solo contiene una referencia a su último elemento para permitir O (1) adición al final de la lista. – akarnokd

1

El método getLast() es de la interfaz Deque que implementa LinkedList. Si quería una versión con respaldo de matriz, podría usar ArrayDeque. Supongo que no forma parte de la interfaz de la Lista porque querían separar diferentes tipos de datos abstractos en interfaces separadas, a pesar de que una implementación como LinkedList puede implementar más de una interfaz.

+0

La interfaz Deque solo se introdujo en Java 6. La clase LinkedList (con su método getLast()) es anterior a esta por varias versiones (introducida en 1.2). –

0

La interfaz de lista no tiene ningún método de conveniencia de ese tipo. No getFirst() o getLast(). Está estrictamente basado en el índice. LinkedList necesita una forma de evitar búsquedas basadas en índices si es posible por razones de rendimiento, por lo que getLast es realmente una optimización, no un método de conveniencia.

Estoy seguro de que los diseñadores querían reducir la cantidad de métodos, por lo que no agregaron muchos métodos potencialmente convenientes.

+1

Si observa la implementación LinkedList, contiene una optimización para el bloqueo basado en índice: si el índice está después del tamaño medio de la lista, entonces el recorrido comienza al final y retrocede. De esta forma, un get (tamaño - 1) tiene prácticamente el mismo costo de tiempo que getLast(). – akarnokd

+0

@ kd304, que es un detalle de implementación que no aborda el caso general de las búsquedas basadas en índices que no son óptimas para LinkedList. No hay ningún contrato en LinkedList que indique que ciertas búsquedas basadas en índices serán rápidas. – Yishai

Cuestiones relacionadas