630 Stimmen

Typ Liste vs. Typ ArrayList in Java

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

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

Ich verstehe, dass mit (1), Implementierungen der Liste Schnittstelle ausgetauscht werden kann. Es scheint, dass (1) typischerweise in einer Anwendung verwendet wird, unabhängig von der Notwendigkeit (ich selbst verwende dies immer).

Ich frage mich, ob jemand (2) verwendet?

Wie oft (und kann ich bitte ein Beispiel bekommen) ist es tatsächlich erforderlich, (1) gegenüber (2) zu bevorzugen (d.h. wo (2) nicht ausreichen würde..aside Kodierung auf Schnittstellen y beste Praktiken usw.)

498voto

kgiannakakis Punkte 100768

Fast immer List ist vorzuziehen gegenüber ArrayList weil zum Beispiel, List kann übersetzt werden in eine LinkedList ohne den Rest der Codebasis zu beeinträchtigen.

Wenn man die ArrayList 代わりに List ist es schwierig, die ArrayList Implementierung in eine LinkedList eine weil ArrayList In der Codebasis wurden spezifische Methoden verwendet, die ebenfalls eine Umstrukturierung erfordern würden.

Sie können sich über die List Implementierungen aquí .

Sie können beginnen mit einer ArrayList , stellen aber bald darauf fest, dass eine andere Umsetzung die bessere Wahl ist.

128voto

Stephen C Punkte 665668

Ich frage mich, ob jemand (2) verwendet?

Ja, aber selten aus gutem Grund (IMO).

Und Menschen werden verbrannt, weil sie die ArrayList wenn sie hätten verwenden sollen List :

  • Utility-Methoden wie Collections.singletonList(...) ou Arrays.asList(...) geben keine ArrayList .

  • Methoden im List API garantiert nicht, dass sie eine Liste desselben Typs zurückgibt.

Zum Beispiel, wenn jemand verbrannt wird, in https://stackoverflow.com/a/1481123/139985 das Poster hatte Probleme mit dem "Slicing", weil ArrayList.sublist(...) gibt keine ArrayList ... und er hatte seinen Code so entworfen, dass er ArrayList als Typ für alle seine Listenvariablen. Am Ende "löste" er das Problem, indem er die Teilliste in eine neue ArrayList .

Das Argument, man müsse wissen, wie die List verhält, wird weitgehend durch die Verwendung der RandomAccess Marker-Schnittstelle. Ja, sie ist ein bisschen klobig, aber die Alternative ist schlimmer.

Und wie oft erfordert die Situation tatsächlich die Verwendung von (1) anstelle von (2) (d. h. wo (2) nicht ausreicht, abgesehen von der "Kodierung nach Schnittstellen" und bewährten Verfahren usw.)

Der Teil "wie oft" der Frage ist objektiv nicht beantwortbar.

(und kann ich bitte ein Beispiel bekommen)

Gelegentlich kann es vorkommen, dass die Anwendung verlangt, dass Sie Methoden in der ArrayList API, die no im List API. Zum Beispiel, ensureCapacity(int) , trimToSize() ou removeRange(int, int) . (Der letzte Punkt tritt nur auf, wenn Sie einen Subtyp von ArrayList erstellt haben, der die Methode als public .)

Das ist der einzige vernünftige Grund für die Codierung in der Klasse und nicht in der Schnittstelle, IMO.

(Es ist theoretisch möglich, dass Sie eine geringfügige Verbesserung der Leistung erhalten ... unter bestimmten Umständen ... auf einigen Plattformen ... aber es lohnt sich nicht, dies zu tun, es sei denn, Sie brauchen wirklich die letzten 0,05%. Dies ist kein triftiger Grund, IMO.)


Man kann keinen effizienten Code schreiben, wenn man nicht weiß, ob der Zufallszugriff effizient ist oder nicht.

Das ist ein berechtigtes Argument. Allerdings bietet Java bessere Möglichkeiten, damit umzugehen; z.B.

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

Wenn Sie das mit einer Liste aufrufen, die nicht die RandomAccess erhalten Sie einen Kompilierungsfehler.

Sie könnten auch dynamisch testen ... mit instanceof ... wenn statische Typisierung zu umständlich ist. Und Sie könnten Ihren Code sogar so schreiben, dass er (dynamisch) unterschiedliche Algorithmen verwendet, je nachdem, ob eine Liste den wahlfreien Zugriff unterstützt oder nicht.

Beachten Sie, dass ArrayList ist nicht die einzige Listenklasse, die Folgendes implementiert RandomAccess . Andere umfassen CopyOnWriteList , Stack y Vector .

Ich habe Leute gesehen, die das gleiche Argument über Serializable (weil List implementiert es nicht) ... aber der obige Ansatz löst auch dieses Problem. (In dem Maße, in dem es überhaupt lösbar über Laufzeittypen. Eine ArrayList wird die Serialisierung fehlschlagen, wenn ein Element nicht serialisierbar ist).


Schließlich werde ich nicht sagen, "weil es guter Stil ist". Dieser "Grund" ist sowohl ein Zirkelschluss (" Warum ist es 'guter Stil'?") und ein Appell an eine nicht genannte (und wahrscheinlich nicht existierende!) höhere Autorität (" Wer sagt, es sei 'guter Stil'?").

(Ich halte es für einen guten Stil, an der Schnittstelle zu programmieren, aber ich werde das nicht als Grund anführen. Es ist besser für Sie, das zu verstehen real und kommen Sie zu den (IMO) richtigen Schlussfolgerungen für sich selbst. Die richtige Schlussfolgerung puede nicht immer dasselbe sein ... je nach Kontext).

48voto

Maxim Shoustin Punkte 77794

Sie könnten zum Beispiel beschließen, eine LinkedList die beste Wahl für Ihre Anwendung ist, aber dann später entscheiden ArrayList könnte aus Leistungsgründen die bessere Wahl sein.

使用する。

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

Anstelle von:

ArrayList list = new ArrayList();

Als Referenz:

enter image description here

(hauptsächlich für das Sammeldiagramm gepostet)

28voto

catch23 Punkte 15336

Sie ist gilt als guter Stil um einen Verweis auf eine HashSet ou TreeSet in einer Variablen vom Typ Set.

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

Auf diese Weise müssen Sie nur eine Zeile ändern, wenn Sie sich für eine TreeSet stattdessen.

Außerdem sollten Methoden, die mit Mengen arbeiten, Parameter vom Typ Menge angeben:

public static void print(Set<String> s)

Dann die Methode kann für alle Set-Implementierungen verwendet werden .

Theoretisch sollten wir die gleiche Empfehlung für verknüpfte Listen aussprechen, nämlich LinkedList-Referenzen in Variablen vom Typ List zu speichern. In der Java-Bibliothek ist die List-Schnittstelle jedoch sowohl für die ArrayList und die LinkedList Klasse. Insbesondere verfügt sie über get- und set-Methoden für den wahlfreien Zugriff, auch wenn diese Methoden für verknüpfte Listen sehr ineffizient sind.

Sie keinen effizienten Code schreiben kann wenn Sie nicht wissen, ob der Direktzugriff effizient ist oder nicht.

Dies ist eindeutig ein schwerwiegender Designfehler in der Standardbibliothek, und ich kann die Verwendung der der List-Schnittstelle aus diesem Grund nicht empfehlen.

Um zu sehen, wie peinlich dieser Fehler ist, werfen Sie einen Blick auf den Quellcode für die binarySearch Methode der Sammlungen Klasse. Diese Methode nimmt einen List-Parameter, aber die binäre Suche macht bei einer verknüpften Liste keinen Sinn. Der Code versucht dann unbeholfen um herauszufinden, ob die Liste eine verknüpfte Liste ist, und wechselt dann zu einer linearen Suche!

El Set Schnittstelle und der Map Schnittstelle, sind gut konzipiert, und Sie sollten sie nutzen.

15voto

Denis Kutlubaev Punkte 13957

Wenn Sie schreiben List sagen Sie eigentlich, dass Ihr Objekt List nur die Schnittstelle, aber Sie geben nicht an, zu welcher Klasse Ihr Objekt gehört.

Wenn Sie schreiben ArrayList geben Sie an, dass Ihre Objektklasse ein größenveränderbares Array ist.

Die erste Version macht Ihren Code also in Zukunft flexibler.

Sehen Sie sich die Java-Dokumente an:

Klasse ArrayList - Resizable-array Implementierung der List Schnittstelle.

Schnittstelle List - Eine geordnete Sammlung (auch als Folge bezeichnet). Der Benutzer dieser Schnittstelle hat eine genaue Kontrolle darüber, wo in der Liste jedes Element eingefügt wird.

Array - Containerobjekt, das eine feste Anzahl von Werten eines einzigen Typs enthält.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X