Type Liste vs type ArrayList en Java

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

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

je comprends qu'avec (1), les implémentations de l'interface List peuvent être échangées. Il semble que (1) est généralement utilisé dans une application indépendamment du besoin (moi je l'utilise toujours).

je me demande si quelqu'un utilise (2)?

aussi, combien de fois (et puis-je s'il vous plaît obtenir un exemple) la situation exige-t-elle réellement l'utilisation de (1) sur (2) (c.-à-d. où (2) Ne suffirait pas..à part codage des interfaces et meilleures pratiques etc.)

468
demandé sur nazar_art 2010-02-17 10:40:13

15 réponses

presque toujours le premier est préféré au second. Le premier présente l'avantage que la mise en œuvre du List peut changer (à un LinkedList par exemple), sans affecter le reste du code. Ce sera une tâche difficile à faire avec un ArrayList , non seulement parce que vous aurez besoin de changer ArrayList à LinkedList partout, mais aussi parce que vous pouvez avoir utilisé ArrayList méthodes spécifiques.

Vous pouvez lire à propos de List implémentations ici . Vous pouvez commencer avec un ArrayList , mais peu de temps après découvrir qu'une autre implémentation est plus appropriée.

378
répondu kgiannakakis 2014-05-21 00:43:14

je me demande si quelqu'un utilise (2)?

Oui. Mais rarement pour une bonne raison (IMO).

et les gens sont brûlés parce qu'ils ont utilisé ArrayList alors qu'ils auraient dû utiliser List :

  • les méthodes de l'Utilitaire comme Collections.singletonList(...) ou Arrays.asList(...) ne pas retourner ArrayList .

  • dans les méthodes L'API List ne garantit pas le retour d'une liste du même type.

Pour l'exemple de quelqu'un se brûler, dans https://stackoverflow.com/a/1481123/139985 l'affiche avait des problèmes de "découpage" parce que ArrayList.sublist(...) ne retourne pas une ArrayList ... et il avait conçu son code pour utiliser ArrayList comme le type de toutes ses variables de liste. Il a fini par "résoudre" le problème en copiant le sous-liste dans une nouvelle ArrayList .

l'argument selon lequel il faut savoir comment se comporte le List est largement résolu en utilisant l'interface marqueur RandomAccess . Oui, c'est un peu maladroit, mais l'alternative est pire.

en outre, à quelle fréquence la situation exige-t-elle réellement l'utilisation de (1) sur (2) (c.-à-d. où (2) Ne suffirait pas..en dehors du "codage aux interfaces" et des meilleures pratiques, etc.)

le " Combien de fois" partie de la question est, objectivement, sans réponse.

(et puis-je s'il vous plaît obtenir un exemple)

à l'occasion, l'application peut exiger que vous utilisiez des méthodes dans L'API ArrayList qui sont et non dans L'API List . Par exemple, ensureCapacity(int) , trimToSize() ou removeRange(int, int) . (Et le dernier ne se produira que si vous avez créé un sous-type D'ArrayList qui déclare la méthode être public .)

C'est la seule bonne raison pour le codage de la classe plutôt que de l'interface, de l'OMI.

(il est théoriquement possible que vous obtiendrez une légère amélioration de performance ... sous certaines circonstances ... sur certaines plates-formes ... mais à moins que vous ayez vraiment besoin de ce dernier 0,05%, cela ne vaut pas la peine de faire cela. Ce n'est pas une bonne raison, IMO.)


You vous ne pouvez pas écrire de code efficace si vous ne savez pas si l'accès aléatoire est efficace ou non.

C'est un point valable. Cependant, Java fournit de meilleures façons de traiter cela; par exemple

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

si vous appelez cela avec une liste qui n'implémente pas RandomAccess vous obtiendrez une erreur de compilation.

, Vous pouvez aussi tester dynamiquement ... en utilisant instanceof ... si la saisie statique est trop embarrassante. Et vous pourriez même écrire votre code pour utiliser des algorithmes différents (dynamiquement) selon si oui ou non une liste supportée l'accès aléatoire.

notez que ArrayList n'est pas la seule classe de liste qui implémente RandomAccess . D'autres comprennent CopyOnWriteList , Stack et Vector .

j'ai vu des gens faire le même argument à propos de Serializable (parce que List ne l'implémente pas) ... mais l'approche ci-dessus résout ce problème aussi. (À l' mesure dans laquelle il est soluble à tous les en utilisant des types d'exécution. Un ArrayList échouera la sérialisation si un élément n'est pas sérialisable.)

99
répondu Stephen C 2018-09-06 01:30:52

par exemple, vous pourriez décider qu'un LinkedList est le meilleur choix pour votre application, mais ensuite décider ArrayList pourrait être un meilleur choix pour des raisons de performance.

Utiliser:

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

au lieu de:

ArrayList list = new ArrayList();

pour référence:

enter image description here

(affiché principalement pour le diagramme de collecte)

35
répondu Maxim Shoustin 2014-09-09 15:12:05

Il est considéré comme bon style pour stocker une référence à un HashSet ou TreeSet dans une variable de type Ensemble.

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

de cette façon, vous devez changer une seule ligne si vous décidez d'utiliser un TreeSet à la place.

en outre, les méthodes qui fonctionnent sur des ensembles doivent spécifier les paramètres de type Set:

public static void print(Set<String> s)

puis la méthode peut être utilisée pour toutes les implémentations .

En théorie, nous devrions faire la même recommandation pour les listes liées, à savoir sauver des Les références de liste de liens dans les variables de liste de type. Cependant, dans la bibliothèque Java, L'interface List est commune aux classes ArrayList et LinkedList . En particulier, il dispose de méthodes get et set pour l'accès aléatoire, même si ces les méthodes sont très inefficaces pour les listes liées.

vous ne pouvez pas écrire de code efficace si vous ne savez pas si l'accès aléatoire est efficace ou non.

il s'agit clairement d'une grave erreur de conception dans la bibliothèque standard, et je ne peux pas recommander l'utilisation l'interface de liste pour cette raison.

pour voir à quel point cette erreur est embarrassante, regardez le code source de la binarySearch méthode de la classe Collections ". Cette méthode nécessite Liste paramètre, mais la recherche binaire n'a pas de sens pour une liste liée. Le code ensuite maladroitement essaie de découvrir si la liste est une liste chaînée, et passe ensuite à une recherche linéaire!

l'interface Set et l'interface Map , sont bien conçues, et vous devez les utiliser.

24
répondu nazar_art 2014-08-16 08:35:03

j'utilise (2) si le code est le "propriétaire" de la liste. Ceci est par exemple vrai pour les variables locales seulement. Il n'y a aucune raison d'utiliser le type abstrait List au lieu de ArrayList . Un autre exemple pour démontrer la propriété:

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
répondu mazatwork 2013-04-21 12:20:37

je pense que les personnes qui utilisent (2) ne connaissent pas le principe de substitution de Liskov ou le Principe D'inversion de dépendance . Ou ils doivent vraiment utiliser ArrayList .

9
répondu True Soft 2010-02-17 07:55:27

quand vous écrivez List , vous dites en fait, que votre objet implémente l'interface List seulement, mais vous ne spécifiez pas à quelle classe votre objet appartient.

lorsque vous écrivez ArrayList , vous spécifiez que votre classe d'objet est un tableau redimensionnable.

ainsi, la première version rend votre code plus flexible à l'avenir.

Regardez Java docs:

Classe ArrayList - mise en œuvre du tableau redimensionnable de l'interface List .

Interface List - une collection ordonnée (également appelée séquence). L'utilisateur de cette interface a un contrôle précis où, dans la liste de chaque élément est inséré.

Array - objet de conteneur qui détient un fixe nombre de valeurs d'un type unique.

9
répondu wzbozon 2013-12-05 17:14:10

en fait il y a des occasions où (2) est non seulement préférable mais obligatoire et je suis très surpris, que personne ne mentionne ceci ici.

Serialization!

si vous avez une classe sérialisable et que vous voulez qu'elle contienne une liste, alors vous devez déclarer que le champ est d'un type concret et sérialisable comme ArrayList parce que l'interface List ne s'étend pas java.io.Serializable

évidemment la plupart des gens ne besoin de sérialisation et oublier cela.

un exemple:

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
répondu raudi 2016-11-14 11:20:47

(3) Collection myCollection = new ArrayList ();

j'utilise ceci typiquement. Et seulement si j'ai besoin des méthodes List, J'utiliserai List. Pareil pour ArrayList. Vous pouvez toujours passer à plus "étroite" de l'interface, mais vous ne pouvez pas passer pour plus "large".

8
répondu dart 2010-02-17 07:51:38

sur les deux suivants:

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

est généralement préférée. Comme vous utiliserez les méthodes de List interface seulement, il vous fournit la liberté d'utiliser une autre mise en œuvre de List par exemple LinkedList à l'avenir. Il vous dissocie donc de l'implémentation spécifique. Maintenant, il y a deux points qui valent la peine d'être mentionnés:

  1. nous devrions toujours programmer l'interface. Plus ici .
  2. vous finirez presque toujours par utiliser ArrayList au lieu de LinkedList . Plus ici .

je me demande si quelqu'un utilise (2)

Oui, parfois (rarement lu). Quand nous avons besoin de méthodes qui font partie de l'implémentation de ArrayList mais pas de l'interface List . Par exemple ensureCapacity .

aussi, comment souvent (et puis-je s'il vous plaît obtenir un exemple) la situation effectivement besoin de l'aide des (1) (2)

presque toujours vous préférez l'option (1). Il s'agit d'un modèle de conception classique en OOP où vous essayez toujours de découpler votre code de l'implémentation spécifique et le programme à l'interface.

6
répondu i_am_zero 2017-05-23 11:33:24

le seul cas où je sais où (2) peut être mieux est lors de L'utilisation de GWT, parce qu'il réduit l'empreinte de l'application (pas mon idée, mais l'équipe Google Web toolkit le dit). Mais pour un java régulier, courir à l'intérieur de la JVM (1) est probablement toujours mieux.

2
répondu Hans Westerbeek 2010-02-17 07:54:29

List est une interface.Il n'y a pas de méthodes. Lorsque vous appelez méthode sur une référence de liste. Il appelle en fait la méthode de ArrayList dans les deux cas.

et pour l'avenir, vous pouvez changer List obj = new ArrayList<> en List obj = new LinkList<> ou un autre type qui met en œuvre interface de liste.

2
répondu Xar-e-ahmer Khan 2015-10-27 11:50:44

Quelqu'un a demandé à nouveau (duplicata) ce qui m'a fait aller un peu plus loin sur cette question.

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 on utilise un viewer bytecode (j'ai utilisé http://asm.ow2.org/eclipse/index.html ) nous avons vu ce qui suit (seulement l'initialisation de liste et l'attribution) pour notre liste extrait:

   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

et pour accédez à la liste :

   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 différence est liste finit par appeler INVOKEINTERFACE tandis que aList calls INVOKEVIRTUAL . D'après le Plugin de référence de Bycode Outline,

invokeinterface est utilisé pour invoquer une méthode déclarée dans un Java interface

alors que invokevirtual

invoque toutes les méthodes sauf interface méthodes (qui utilisent invokeinterface), les méthodes statiques (qui utilisent invokestatic), et les quelques cas spéciaux traités par invokespecial.

En résumé, invokevirtual pop objectref hors de la pile alors que pour invokeinterface

l'interpréteur sort ' n ' items de la pile des opérandes, où 'n' est un 8-bit non signé paramètre entier tiré du bytecode. Le premier de ces éléments est objectref, une référence à l'objet dont la méthode est appelée.

si je comprends bien cela, la différence est essentiellement comment chaque chemin récupère objectref .

2
répondu toxicafunk 2016-12-23 11:06:00

je dirais que 1 est préférable, sauf si

  • vous dépendez de l'implémentation du comportement optionnel* dans ArrayList, dans ce cas l'utilisation explicite de ArrayList est plus claire
  • vous utiliserez L'ArrayList dans un appel de méthode qui nécessite ArrayList, éventuellement pour un comportement optionnel ou des caractéristiques de performance

ma conjecture est que dans 99% des cas, vous pouvez obtenir avec la liste, qui est préféré.

  • par exemple removeAll , ou add(null)
1
répondu extraneon 2010-02-17 07:47:58

List interface ont plusieurs classes différentes - ArrayList et LinkedList . LinkedList est utilisée pour créer des collections indexées et ArrayList - pour créer des listes triées. Ainsi, vous pouvez utiliser l'une dans vos arguments, mais vous pouvez permettre à d'autres développeurs qui utilisent votre code, bibliothèque, etc. pour utiliser différents types de listes, non seulement que vous utilisez, ainsi, dans cette méthode

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

vous pouvez l'utiliser seulement avec ArrayList , pas LinkedList , mais vous pouvez permettre d'utiliser n'importe laquelle des classes List sur d'autres endroits où la méthode utilise, c'est juste votre choix, donc utiliser une interface peut le permettre:

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

dans cette méthode arguments vous pouvez utiliser n'importe laquelle des classes List que vous voulez utiliser:

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

list.add ("string");

myMethod (list);

CONCLUSION:

utilisez les interfaces partout où c'est possible, ne vous limitez pas à utiliser différents les méthodes qu'ils veulent utiliser.

0
répondu Acuna 2018-06-15 21:43:29