2010-10-20 15 views
6

El desarrollo de Java, que siempre ha aprendido que su mejor momento para crear un ArrayList mediante la interfaz de lista como el tipo de la variable de la lista se almacena en. Al igual que este¿Por qué la mayoría de los ejemplos que usan ArrayList

List<String> myList = new ArrayList<String>(); 

Sin embargo, al observar muchos de los ejemplos de Android que se incluyen en el paquete, han creado la lista utilizando la clase.

ArrayList<String> myList = new ArrayList<String>(); 

¿Hay alguna razón para que esto se haga? ¿Es más rápido, más ligero o algo para establecer explícitamente la clase?

+1

"siempre ha aprendido que es mejor crear una ArrayList utilizando la interfaz de lista". Curiosamente, nunca había escuchado eso antes. ¿Cuál es la ventaja de hacerlo? –

+5

@Brian si usa la interfaz tanto como sea posible, el código se desacoplará de la implementación de la lista. Facilitando la sustitución de la lista por otro objeto que solo implemente la interfaz de la lista. Como una extraña abstracción de base de datos u otra cosa. – Janusz

+0

¿Cuándo los ejemplos siempre usaron las mejores prácticas? La mayoría de los ejemplos de MySQL/PHP están plagados de ataques de inyección SQL y otros problemas. La mayoría de los ejemplos en la web se realizan de tal manera que obtienes la mayor funcionalidad posible del código más bajo posible o con un código tan fácil de entender como sea posible, ignorando por completo las mejores prácticas, los posibles agujeros de seguridad y la comprobación de errores . – Kibbee

Respuesta

9

En entornos con recursos limitados, como los teléfonos para los que está diseñado Android, es preferible evitar el uso de la interfaz, ya que implica una llamada de función virtual adicional.

+1

Esto es lo que siempre he escuchado como explicación, aunque me interesaría ver la diferencia en términos de rendimiento en el mundo real (desde la perspectiva del usuario). –

+2

Estoy de acuerdo. Tendría que haber MUCHAS llamadas de constructor para que esto haga la diferencia. Me interesaría ver una aplicación práctica que haga suficientes llamadas para que haya un beneficio de rendimiento. Pero, de nuevo, nunca he programado para Android ... –

+2

@Daniel Standage: No creo que el uso de tipo explícito realmente afecte las llamadas al constructor; el "impuesto de interfaz" (llamada de función virtual) se paga por cada llamada a funciones miembro. El propio constructor apenas se ve afectado. Sin embargo, estoy de acuerdo en que tomaría un punto de referencia real para ver si esto realmente importa ya que los últimos teléfonos inteligentes tienen procesadores bastante poderosos con una cantidad decente de memoria. –

0

No hay mucha diferencia, excepto que en el ejemplo de Android están usando un tipo mucho más explícito. Quizás haya métodos de Android que confían en recibir una ArrayList en lugar de solo una Lista.

2

Si bien puede haber un beneficio de rendimiento, es casi seguro pequeño y un ejemplo de optimización prematura.

Una explicación mucho más probable es que el escritor de los ejemplos quiere mantenerlo enfocado en lo que él (o ella) está tratando de enseñarle. Mantener la variable y el objeto al que hace referencia del mismo tipo es una cosa menos en la que pensar. Los ejemplos de código publicados son un tipo de código cuando está seguro de que nunca tendrá que modificarlo, y en ese caso está perfectamente bien utilizar ArrayList como tipo de variable.

12

Recomendaría leer Performance Myths, lo que explica cuáles son las ventajas y los problemas de definir una variable como List o ArrayList.

+0

¡Excelente enlace! Demostraron que para dispositivos que no tienen JIT es 6% más rápido usar HashMap directamente en lugar de Map (que es HashMap) –

+0

Y, solía haber una página en los documentos que especificaba que para Android DEBES EVITAR la interfaz tipos (porque, por supuesto, antes del JIT, era más lento). De todos modos, es por eso que ve tantos ejemplos de Android como ese, fue defendido por la documentación hasta hace poco. –

10

Personalmente, creo que la cosa "siempre has aprendido" pasa por alto el punto. Esto:

List<String> myList = new ArrayList<String>(); 

tiene muy poco beneficio de mantenimiento. Si desea cambiarlo a una implementación diferente, sigue siendo solo una línea para cambiar si usa el tipo de implementación. Sin embargo, una cuestión totalmente diferente es la siguiente:

public void process(List<String> list) { 

este caso, utilizando la interfaz que realmente importa ya que permite a las personas que llaman al método a utilizar diferentes implementaciones sin tener que cambiar la firma del método (que es posible que no puedan hacerlo).

+0

Estoy totalmente de acuerdo con eso +1 –

+0

+1 Es hora de poner fin a esta simulación. Adjunté mis explicaciones. – irreputable

+0

'List myList = new ArrayList()' documenta el intento de myList, ya que el código espera usar myList como 'List' y no específicamente' ArrayList'. –

1

Al compilar el código que opera en los objetos List <>, el compilador debe usar llamadas de interfaz, que se preocupan más que las llamadas virtuales regulares en tipos concretos.

Por supuesto, en un fragmento de código donde el compilador ve el objeto que se está instanciando y puede probar que algo que se declara como una Lista <> es siempre un ArrayList <>, el compilador debería poder emitir un llamada regular en lugar de una llamada de interfaz, pero los métodos que no están en línea y operan en objetos ya instanciados no se beneficiarían de esta optimización.

La optimización de forma ubicua que acelera las llamadas virtuales, es decir, las memorias caché en línea (con frecuencia polimórfica Inline almacenamiento en caché o PIC, que no debe confundirse con el código independiente de posición), se beneficia de la observación de que las instancias de una única subclase se accede siempre a través una variable de cierto tipo declarado. En este escenario, después de que el código se haya estado ejecutando por un tiempo, el JIT puede adivinar de manera optimista que un objeto List <> solo será una ArrayList <>, generará una trampa en caso de que la suposición sea incorrecta y fracasará con ArrayList <> llamada.

Los procesadores modernos ejecutan la verificación muy rápidamente (porque son superescalares y tienen una buena predicción de bifurcación) por lo que no se da cuenta del costo de todas las llamadas a la llamada virtual e interfaz de implementación individual. Pero hace que la máquina virtual trabaje duro, instrumentando, generando y parcheando todo ese código.

Para el software de servidor que se ejecuta en estado estable en HotSpot es irrelevante, pero para un inicio rápido en un dispositivo móvil podría marcar la diferencia. No sé cuán buena es la VM de Google.

Un artículo agradable sobre él por el Dr. acantilado Click, Jr. (personalizado grande de hardware hierro, hotspot-deived VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization

Y, por supuesto, "el almacenamiento en caché en línea" en la wikipedia.

0

Es un conocido "Principio de diseño" sobre cómo hacer un buen diseño, "programar en una interfaz, no en una implementación". Como dijo Michael Borgwardt, tal vez no le importe usar este principio con las variables locales, pero si tiene asociaciones entre tipos, entonces tiene sentido programar las interfaces en lugar de la implementación para usar los beneficios del OOP . Implementar en interfaces en lugar de impl permite asignaciones polimórficas dinámicas en tiempo de ejecución. decir,

interface IAa { say(); } 
class A implements IAa { public void say() { .. says A ... } } 
class B implements IAa { public void say() { .. says B ... } } 


class App { 

public void someFoo() { 
    IAa myA = new A(); 
    myA.say(); // says A 
    myA = new B(); 
    myA.say(); // says B 

} 
... 
} 

no creo que sería diferente en la programación Android :)

1

El patrón Interface x = new Implementation() es un ingenuo intento de ser popular en abstracto.

Una declaración de variable y declaración de inicialización, Type x = new Constructor();, es definitivamente parte del detalle de implementación. No es parte de una API pública (a menos que sea public final, pero la lista es mutable, así que es inapropiado)

Como detalle de implementación, ¿a quién intentamos engañar con esta "abstracción"? Es mejor mantener el tipo lo más específico posible. ¿Es una lista de arreglos o una lista vinculada? ¿Debería ser seguro o no? La elección es importante para la implementación, elegimos cuidadosamente la lista específica impl. Entonces lo declaramos como una mera Lista como si no importara y no nos importa?

La única razón legítima para declararlo como List es Soy demasiado flojo para escribir. Eso también cubre el argumento de que si necesito moverme a otra lista impl, tengo un lugar menos para modificar.

Este motivo es legítimo solo cuando el alcance de la variable es pequeño, y podemos ver todos sus usos de un solo vistazo. De lo contrario, declare el tipo más específico, de modo que su rendimiento y características semánticas se manifiesten en todo el código que usa la variable.

Cuestiones relacionadas