Tipo Lista vs tipo ArrayList en Java


(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

Entiendo que con (1), las implementaciones de la interfaz List se pueden intercambiar. Parece que (1) se usa típicamente en una aplicación independientemente de la necesidad (yo siempre uso esto).

Me pregunto si alguien usa (2)?

Además, ¿con qué frecuencia (y puedo obtener un ejemplo) la situación realmente requiere usar (1) sobre (2) (es decir, donde (2) no sería suficiente..a un lado codificación de interfaces y las mejores prácticas sucesivamente.)

Author: nazar_art, 2010-02-17

15 answers

Casi siempre se prefiere el primero sobre el segundo. El primero tiene la ventaja de que la implementación del List puede cambiar (a un LinkedList por ejemplo), sin afectar el resto del código. Esta será una tarea difícil de hacer con un ArrayList, no solo porque tendrá que cambiar ArrayList a LinkedList en todas partes, sino también porque puede haber utilizado ArrayList métodos específicos.

Puedes leer sobre List implementaciones aquí. Usted puede comenzar con un ArrayList, pero poco después descubre que otra implementación es más apropiada.

 378
Author: kgiannakakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-21 00:43:14

Me pregunto si alguien usa (2)?

Sí. Pero rara vez por una buena razón (IMO).

Y la gente se quema porque usaron ArrayList cuando deberían haber usado List:

  • Los métodos de utilidad como Collections.singletonList(...) o Arrays.asList(...) no devuelven un ArrayList.

  • Los métodos en la API List no garantizan devolver una lista del mismo tipo.

Por ejemplo, de alguien que se quema, en https://stackoverflow.com/a/1481123/139985 el póster tuvo problemas con "rebanar" porque ArrayList.sublist(...) no devuelve un ArrayList... y había diseñado su código para usar ArrayList como el tipo de todas sus variables de lista. Terminó "resolviendo" el problema copiando la sublista en un nuevo ArrayList.

El argumento de que necesita saber cómo se comporta List se aborda en gran medida utilizando la interfaz de marcadores RandomAccess. Sí, es un poco torpe, pero la alternativa es peor.

También, con qué frecuencia la situación realmente requiere usar (1) sobre (2) (es decir, donde (2) no sería suficiente..aparte de "codificación a interfaces" y mejores prácticas, etc.)

La parte de "con qué frecuencia" de la pregunta es objetivamente sin respuesta.

(y puedo por favor obtener un ejemplo)

Ocasionalmente, la aplicación puede requerir que use métodos en la API ArrayList que son no en la API List. Por ejemplo, ensureCapacity(int), trimToSize() o removeRange(int, int). (Y el último solo surgirá si ha creado un subtipo de ArrayList que declara que el método es public.)

Esa es la única razón de sonido para codificar a la clase en lugar de la interfaz, IMO.

(Es teóricamente posible que obtendrá una ligera mejora en el rendimiento ... bajo ciertas circunstancias ... en algunas plataformas ... pero a menos que realmente necesite ese último 0.05%, no vale la pena hacer esto. Esta no es una buena razón, IMO.)


No puede escribir código eficiente si no sabe si el acceso aleatorio es eficiente o no.

Ese es un punto válido. Sin embargo, Java proporciona mejores maneras de lidiar con eso; por ejemplo,

public <T extends List & RandomAccess> void test(T list) {
    // do stuff
}

Si llama a eso con una lista que no implementa RandomAccess obtendrá un error de compilación.

También puedes probar dinámicamente ... usando instanceof... si la escritura estática es demasiado incómoda. E incluso podría escribir su código para usar diferentes algoritmos (dinámicamente) dependiendo de si una lista admite o no el acceso aleatorio.

Tenga en cuenta que ArrayList no es la única clase list que implementa RandomAccess. Otros incluyen CopyOnWriteList, Stack y Vector.

He visto a gente hacer el mismo argumento sobre Serializable (porque List no lo implementa) ... pero el enfoque anterior también resuelve este problema. (En la medida en que sea solucionable en absoluto usando tipos de tiempo de ejecución. Un ArrayList fallará la serialización si cualquier elemento no es serializable.)

 99
Author: Stephen C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-06 01:30:52

Por ejemplo, podría decidir que LinkedList es la mejor opción para su aplicación, pero luego decidir ArrayList podría ser una mejor opción por razones de rendimiento.

Uso:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

En lugar de:

ArrayList list = new ArrayList();

Para referencia:

introduzca la descripción de la imagen aquí

(publicado principalmente para diagrama de colección)

 35
Author: Maxim Shoustin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-09 15:12:05

Es considerado buen estilo para almacenar una referencia a un HashSet o TreeSet en una variable de tipo Set.

Set<String> names = new HashSet<String>();

De esta manera, solo tiene que cambiar una línea si decide usar un TreeSet en su lugar.

Además, los métodos que operan en conjuntos deben especificar parámetros de tipo Set:

public static void print(Set<String> s)

Entonces el método se puede utilizar para todas las implementaciones establecidas.

En teoría, deberíamos hacer lo mismo recomendación para las listas vinculadas, es decir, para guardar LinkedList referencias en variables de tipo List. Sin embargo, en la biblioteca Java, la interfaz List es común tanto a la clase ArrayList como a la clase LinkedList. En particular, tiene métodos get y set para el acceso aleatorio, a pesar de que estos métodos son muy ineficientes para las listas enlazadas.

Usted no se puede escribir código eficiente si no sabe si el acceso aleatorio es eficiente o no.

Esto es claramente un diseño serio error en la biblioteca estándar, y no puedo recomendar el uso la interfaz de la lista por esa razón.

Para ver lo embarazoso que es ese error, echa un vistazo a el código fuente para el método binarySearch de la Colecciones clase. Ese método toma una Parámetro List, pero la búsqueda binaria no tiene sentido para una lista enlazada. El código entonces torpemente intenta descubrir si la lista es una lista vinculada, y luego cambia a una búsqueda lineal!

La interfaz Set y el Map interfaz, están bien diseñados, y usted debe usarlos.

 24
Author: nazar_art,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-16 08:35:03

Utilizo (2) si el código es el "propietario" de la lista. Esto es, por ejemplo, cierto para las variables de solo local. No hay razón para usar el tipo abstracto List en lugar de ArrayList. Otro ejemplo para demostrar la propiedad:

public class Test {

    // This object is the owner of strings, so use the concrete type.
    private final ArrayList<String> strings = new ArrayList<>();

    // This object uses the argument but doesn't own it, so use abstract type.
    public void addStrings(List<String> add) {
        strings.addAll(add);
    }

    // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
    public List<String> getStrings() {
        return Collections.unmodifiableList(strings);
    }

    // Here we create a new list and give ownership to the caller. Use concrete type.
    public ArrayList<String> getStringsCopy() {
        return new ArrayList<>(strings);
    }
}
 11
Author: mazatwork,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-21 12:20:37

Creo que la gente que usa (2) no conoce el principio de sustitución de Liskovo el principio de inversión de dependencia. O realmente tienen que usar ArrayList.

 9
Author: True Soft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 07:55:27

Cuando escribes List, realmente dices que tu objeto implementa solo la interfaz List, pero no especificas a qué clase pertenece tu objeto.

Cuando escribe ArrayList, especifica que su clase de objeto es una matriz de tamaño variable.

Por lo tanto, la primera versión hace que su código sea más flexible en el futuro.

Mira los documentos de Java:

Clase ArrayList - Resizable - implementación de matriz de la interfaz List.

Interfaz List - An colección ordenada (también conocida como secuencia). El usuario de esta interfaz tiene un control preciso sobre dónde en la lista se inserta cada elemento.

Array - objeto contenedor que contiene un número fijo de valores de un solo tipo.

 9
Author: wzbozon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-05 17:14:10

En realidad hay ocasiones en las que (2) no solo es preferido sino obligatorio y estoy muy sorprendido de que nadie mencione esto aquí.

Serialización!

Si tiene una clase serializable y desea que contenga una lista, debe declarar que el campo es de un tipo concreto y serializable como ArrayList porque la interfaz List no se extiende java.io.Serializable

Obviamente la mayoría de las personas no necesitan serialización y se olvidan de esto.

Un ejemplo:

public class ExampleData implements java.io.Serializable {

// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();
 9
Author: raudi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-14 11:20:47

(3) Collection MyCollection = new ArrayList ();

Estoy usando esto típicamente. Y solo si necesito métodos de Lista, usaré Lista. Mismo con ArrayList. Siempre puede cambiar a una interfaz más "estrecha", pero no puede cambiar a más"amplia".

 8
Author: dart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 07:51:38

De los dos siguientes:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

Primero es generalmente preferido. Como solo utilizará métodos de la interfaz List, le proporciona la libertad de usar alguna otra implementación de List, por ejemplo, LinkedList en el futuro. Así que te desacopla de una implementación específica. Ahora hay dos puntos que vale la pena mencionar:

  1. Siempre debemos programar para la interfaz. Más aquí.
  2. Casi siempre terminarás usando ArrayList sobre LinkedList. Mas aquí.

Me pregunto si alguien usa (2)

Sí a veces (leer raramente). Cuando necesitamos métodos que son parte de la implementación de ArrayList pero no parte de la interfaz List. Por ejemplo ensureCapacity.

Además, ¿con qué frecuencia (y puedo por favor obtener un ejemplo) la situación en realidad requieren usar (1) sobre (2)

Casi siempre se prefiere la opción (1). Este es un patrón de diseño clásico en OOP donde siempre intentas desacoplar el código de la implementación específica y el programa a la interfaz.

 6
Author: i_am_zero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:24

El único caso del que soy consciente de dónde (2) puede ser mejor es cuando se usa GWT, porque reduce la huella de la aplicación (no es mi idea, pero el equipo de Google web toolkit lo dice). Pero para java regular que se ejecuta dentro de la JVM (1) es probablemente siempre mejor.

 2
Author: Hans Westerbeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 07:54:29

La lista es una interface.It no tiene métodos. Cuando llama al método en una referencia de lista. De hecho, llama al método de ArrayList en ambos casos.

Y para el futuro puede cambiar List obj = new ArrayList<> a List obj = new LinkList<> u otro tipo que implemente la interfaz de lista .

 2
Author: Xar-e-ahmer Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-27 11:50:44

Alguien volvió a preguntar esto (duplicado), lo que me hizo profundizar un poco más en este tema.

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");

    ArrayList<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");

}

Si usamos un visor de bytecode (usé http://asm.ow2.org/eclipse/index.html ) vemos lo siguiente (solo inicialización de lista y asignación) para nuestro fragmento list :

   L0
    LINENUMBER 9 L0
    NEW ArrayList
    DUP
    INVOKESPECIAL ArrayList.<init> () : void
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 1: list
    LDC "a"
    INVOKEINTERFACE List.add (Object) : boolean
    POP
   L2
    LINENUMBER 11 L2
    ALOAD 1: list
    LDC "b"
    INVOKEINTERFACE List.add (Object) : boolean
    POP

Y para alist :

   L3
    LINENUMBER 13 L3
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 2
   L4
    LINENUMBER 14 L4
    ALOAD 2
    LDC "a"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L5
    LINENUMBER 15 L5
    ALOAD 2
    LDC "b"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP

La diferencia es list termina llamando INVOKEINTERFACE, mientras que aList llamadas INVOKEVIRTUAL. Accoding a la referencia del complemento Bycode Outline,

Invokeinterface se utiliza para invocar un método declarado dentro de un Java interfaz

Mientras invokevirtual

Invoca todos los métodos excepto los métodos de interfaz (que utilizan invokeinterface), los métodos estáticos (que utilizan invokestatic), y los pocos casos especiales atendidos por invokespecial.

En resumen, invokevirtual pops objectref fuera de la pila mientras que para invokeinterface

El intérprete saca' n 'elementos de la pila de operando, donde' n ' es un 8 bits sin signo parámetro entero tomado del bytecode. El primero de estos elementos es objectref, una referencia al objeto cuyo método está siendo llamado.

Si entiendo esto correctamente, la diferencia es básicamente cómo cada camino recupera objectref.

 2
Author: toxicafunk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 11:06:00

Yo diría que se prefiere 1, a menos que

  • depende de la implementación del comportamiento opcional* en ArrayList, en ese caso usar explícitamente ArrayList es más claro
  • Utilizará ArrayList en una llamada a un método que requiere ArrayList, posiblemente para características opcionales de comportamiento o rendimiento

Mi conjetura es que en el 99% de los casos, usted puede obtener la Lista de preferidos.

  • por ejemplo removeAll, o add(null)
 1
Author: extraneon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 07:47:58

List interfaz tiene varias clases diferentes - ArrayList y LinkedList. LinkedList se utiliza para crear colecciones indexadas y ArrayList - para crear listas ordenadas. Por lo tanto, puede usar cualquiera de ellos en sus argumentos, pero puede permitir a otros desarrolladores que usen su código, biblioteca, etc. para utilizar diferentes tipos de listas, no solo que se utiliza, por lo tanto, en este método

ArrayList<Object> myMethod (ArrayList<Object> input) {
   // body
}

Puede usarlo solo con ArrayList, no LinkedList, pero puede permitir el uso de cualquiera de las clases List en otros lugares donde el método está utilizando, es solo tu elección, por lo que usar una interfaz puede permitirlo:

List<Object> myMethod (List<Object> input) {
   // body
}

En los argumentos de este método puedes usar cualquiera de las clases List que quieras usar:

List<Object> list = new ArrayList<Object> ();

list.add ("string");

myMethod (list);

CONCLUSIÓN:

Use las interfaces en todas partes cuando sea posible, no restrinja a usted o a otros a usar diferentes métodos que ellos quieran usar.

 0
Author: Acuna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-15 21:43:29