2010-10-20 12 views
6

Lo sviluppo di Java, si è sempre saputo che il suo meglio per creare un ArrayList utilizzando l'interfaccia List come tipo per la variabile della lista viene memorizzato in. Ti piace questaPerché sono la maggior parte degli esempi utilizzando ArrayList

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

Tuttavia, esaminando molti degli esempi di Android inclusi nel pacchetto, hanno creato l'elenco utilizzando la classe.

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

C'è qualche motivo per farlo? È più veloce, più leggero o qualcosa da impostare esplicitamente la classe?

+1

"hai sempre imparato che è meglio creare un ArrayList utilizzando l'interfaccia Elenco". È interessante notare che non l'ho mai sentito prima. Qual è il vantaggio di farlo? –

+5

@Brian se si utilizza l'interfaccia il più possibile il codice verrà disaccoppiato dall'implementazione dell'elenco. Rendendo più facile la sostituzione della lista con un altro oggetto che implementa solo l'interfaccia della lista. Come qualche strana astrazione di database o qualcos'altro. – Janusz

+0

Quando gli esempi hanno sempre utilizzato le best practice? La maggior parte degli esempi MySQL/PHP sono pieni di attacchi SQL injection e altri problemi. La maggior parte degli esempi sul Web viene eseguita in modo tale da ottenere il massimo di funzionalità possibile dal meno codice possibile, o dal codice più semplice da comprendere possibile, ignorando completamente le best practice, i potenziali buchi di sicurezza e il controllo degli errori . – Kibbee

risposta

9

In un ambiente con risorse limitate come i telefoni per cui è stata progettata Android, è preferibile evitare l'utilizzo di Interfaccia, poiché implica una chiamata di funzione virtuale aggiuntiva.

+1

Questo è quello che ho sempre sentito come spiegazione, anche se sarei interessato a vedere la differenza in termini di prestazioni del mondo reale (dal punto di vista dell'utente). –

+2

Sono d'accordo. Dovrebbero esserci MOLTE richieste di costruzione per fare la differenza. Sarei interessato a vedere un'applicazione pratica che fa abbastanza richieste affinché ci sia un vantaggio in termini di prestazioni. Ma poi di nuovo non ho mai programmato per Android ... –

+2

@Daniel Standage: Non penso che l'uso del tipo esplicito in effetti influenzi le chiamate del costruttore; la "tassa di interfaccia" (chiamata di funzione virtuale) viene pagata per ogni chiamata alle funzioni membro. Il costruttore stesso è a malapena influenzato. Tuttavia, sono d'accordo sul fatto che ci vorrebbe un benchmark effettivo per vedere se questo è ancora importante dal momento che gli ultimi smartphone hanno processori abbastanza potenti con una buona quantità di memoria. –

0

Non c'è molta differenza, tranne che nell'esempio Android stanno usando un tipo molto più esplicito. Forse ci sono metodi Android che si basano sulla ricezione di un ArrayList piuttosto che di un elenco.

2

Mentre ci può essere un vantaggio in termini di prestazioni, è quasi certamente piccolo e un esempio di ottimizzazione prematura.

Una spiegazione molto più probabile è che lo scrittore degli esempi vuole tenerti concentrato sulla cosa che lui (o lei) sta cercando di insegnarti. Mantenere la variabile e l'oggetto a cui fa riferimento lo stesso tipo è una cosa in meno a cui pensare. Gli esempi di codice pubblicati sono un tipo di codice quando sei abbastanza sicuro di non doverlo modificare, e in tal caso è perfettamente corretto usare ArrayList come tipo di variabile.

12

io consiglierei di leggere Performance Myths, che spiega quali sono i vantaggi ei problemi di definizione di una variabile come elenco o ArrayList.

+0

Ottimo link! Hanno dimostrato che per i dispositivi che non hanno JIT è più veloce del 6% usare HashMap direttamente anziché Map (che è HashMap) –

+0

E, c'era una pagina nei documenti che specificava esplicitamente che per Android dovresti EVITARE l'interfaccia tipi (perché, naturalmente, prima della JIT, era più lento). Ad ogni modo, è per questo che vedi così tanti esempi di Android come questo, è stato sostenuto dalla documentazione fino a poco tempo fa. –

10

Personalmente, credo che la cosa "hai sempre imparato" non coglie il punto. Questo:

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

ha pochissimi vantaggi di manutenzione. Se si desidera cambiarlo in un'implementazione diversa, è ancora solo una riga da modificare se si utilizza il tipo di implementazione. Tuttavia, un totalmente questione diversa è questa:

public void process(List<String> list) { 

Qui, utilizzando l'interfaccia che conta davvero perché permette alle persone che chiamano il metodo di utilizzare diverse implementazioni senza dover cambiare la firma del metodo (che potrebbero non essere in grado di farlo).

+0

Concordo pienamente con questo +1 –

+0

+1 È ora di terminare questa finzione. Ho allegato le mie spiegazioni. – irreputable

+0

'List myList = new ArrayList()' documenta l'intento di myList, in quanto il codice si aspetta di usare myList come 'List' e non specificamente come' ArrayList'. –

1

Quando si compila il codice che opera su List <> oggetti, il compilatore deve utilizzare le chiamate di interfaccia, che si preoccupano più costoso di chiamate virtuali regolari sui tipi concreti.

Naturalmente, in un pezzo di codice in cui il compilatore vede l'oggetto che viene istanziato e può dimostrare che qualcosa che è dichiarato essere un elenco <> è sempre un ArrayList <>, il compilatore dovrebbe essere in grado di emettere solo un chiamata normale invece di una chiamata di interfaccia, ma i metodi che non sono in linea e funzionano su oggetti già istanziati non beneficerebbero di questa ottimizzazione.

L'ottimizzazione ubiquitaria che accelera le chiamate virtuali, ovvero le cache in linea (spesso la cache in linea polimerica o il PIC, da non confondere con il codice di posizione indipendente), trae vantaggio dall'osservazione che le istanze di una sola sottoclasse sono mai accessibili attraverso una variabile di un certo tipo dichiarato. In questo scenario, dopo che il codice è stato eseguito per un po ', il JIT può supporre ottimisticamente che un oggetto List <> sarà sempre solo un ArrayList <>, genererà una trappola nel caso in cui l'ipotesi fosse sbagliata e ricada con ArrayList <> chiamata.

I moderni processori eseguono il controllo molto rapidamente (perché sono superscalari e hanno una buona previsione di ramo) in modo da non notare il costo di tutte quelle chiamate virtuali e chiamate di interfaccia di implementazione singola. Ma rende la VM lavorare duro, strumentare, generare e applicare patch a tutto quel codice.

Per il software server in esecuzione in stato stazionario su HotSpot è irrilevante, ma per l'avvio veloce su un dispositivo mobile che potrebbe fare la differenza - non so quanto è buono VM di Google.

Un bel articolo su di esso dal dottor Cliff Click, Jr. (grande personalizzato ferro hardware, hotspot-deived VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization

E naturalmente "inline caching" su wikipedia.

0

È un noto "principio di progettazione" su come realizzare un buon progetto, "programma su un'interfaccia, non un'implementazione". Come ha detto Michael Borgwardt, forse non si cura di utilizzare questo principio con le variabili locali, ma se si hanno associazioni tra tipi, allora ha senso programmare su interfacce piuttosto che implementare per utilizzare i vantaggi di OOP. Implementare interfacce anziché impl consente assegnazioni polimorfiche dinamiche in runtime. dire,

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 

} 
... 
} 

non credo che, sarebbe diverso nella programmazione Android :)

1

Il Interface x = new Implementation() modello è un tentativo ingenuo popolare di essere astratto.

una dichiarazione di variabile e dichiarazione inizializzazione, Type x = new Constructor();, è sicuramente parte di dettaglio attuazione. Non fa parte di un'API pubblica (a meno che non sia public final, ma List è mutabile e quindi inappropriata)

Come dettaglio di implementazione, chi stiamo cercando di imbrogliare con questa "astrazione"? È meglio mantenere il tipo più specifico possibile. Si tratta di un elenco di array o di un elenco collegato? Dovrebbe essere thread sicuro o no? La scelta è importante per l'implementazione, abbiamo scelto con cura l'elenco specifico impl. Quindi lo dichiariamo come una semplice lista come se non contasse e non ci interessa?

L'unico motivo valido per dichiararlo come Elenco è Sono troppo pigro per digitare. Ciò copre anche l'argomento che se ho bisogno di spostarmi su un altro elenco impl ho uno spazio in meno per modificare.

Questo motivo è legittimo solo quando lo scope variabile è piccolo, e possiamo vedere tutti i suoi usi da un solo sguardo. Altrimenti, dichiara il tipo più specifico, in modo che le sue prestazioni e le sue caratteristiche semantiche si manifestino su tutto il codice che utilizza la variabile.

Problemi correlati