Question Type de liste vs type ArrayList en Java


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

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

Je comprends cela avec (1), les implémentations du liste l'interface peut être permutée. Il semble que (1) est généralement utilisé dans une application indépendamment du besoin (moi-même je l'utilise toujours).

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

En outre, à quelle fréquence (et puis-je avoir un exemple) la situation nécessite-t-elle réellement d'utiliser (1) sur (2) (c'est-à-dire où (2) ne suffirait pas ... à côté codage aux interfaces et les meilleures pratiques etc.)


459
2018-02-17 07:40


origine


Réponses:


Presque toujours le premier est préféré au second. Le premier a l'avantage que la mise en œuvre de la List peut changer (à un LinkedList par exemple), sans affecter le reste du code. Ce sera une tâche difficile à faire avec un ArrayListnon seulement parce que vous devrez changer ArrayList à LinkedList partout, mais aussi parce que vous avez peut-être utilisé ArrayList méthodes spécifiques.

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


371
2018-02-17 07:46



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

Oui. Mais rarement pour une bonne raison.

Et parfois, les gens se brûlent parce qu'ils ont utilisé ArrayList quand ils auraient dû utiliser List:

  • Méthodes d'utilité comme Collections.singletonList(...) ou Arrays.asList(...) ne retourne pas un ArrayList.

  • Méthodes dans le List L'API ne garantit pas de renvoyer une liste du même type.

Par exemple, dans https://stackoverflow.com/a/1481123/139985 l'affiche a eu des problèmes avec "trancher" parce que ArrayList.sublist(...) ne retourne pas un ArrayList ... et il avait conçu son code pour l'utiliser ArrayList comme le type de toutes ses variables de liste. Il a fini par "résoudre" le problème en copiant la sous-liste dans un nouveau ArrayList.

L'argument que vous avez besoin de savoir comment le List se comporte en grande partie en utilisant le RandomAccess interface de marqueur. Oui, c'est un peu maladroit, mais l'alternative est pire.

De même, à quelle fréquence la situation nécessite-t-elle réellement d'utiliser (1) sur (2) (c'est-à-dire où (2) ne suffirait pas ... à côté du «codage des interfaces» et des meilleures pratiques etc.

La partie «combien de fois» de la question est objectivement irréfutable.

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

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

C'est la seule raison valable de coder pour la classe plutôt que pour l'interface, IMO.

(Il est théoriquement possible que vous obteniez une légère amélioration des performances ... dans certaines circonstances ... sur certaines plates-formes ... mais à moins que vous ayez vraiment besoin de ces 0,05%, cela ne vaut pas la peine de le faire. raison sonore, IMO.)


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 une remarque véridique. Cependant, Java fournit de meilleurs moyens de gérer cela. par exemple.

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

Si vous appelez cela avec une liste qui ne met pas en œuvre RandomAccess vous obtiendrez une erreur de compilation.

Vous pouvez également tester dynamiquement ... en utilisant instanceof ... si le typage statique est trop délicat. Et vous pouvez même écrire votre code pour utiliser différents algorithmes (dynamiquement) selon que la liste supporte ou non l'accès aléatoire.

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

J'ai vu des gens faire le même argument à propos de Serializable (car List ne l'implémente pas) ... mais l'approche ci-dessus résout aussi ce problème. (Dans la mesure où c'est soluble du tout en utilisant des types d'exécution. Un ArrayList échouera la sérialisation si un élément n'est pas sérialisable.)


98
2018-02-17 07:50



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

Utilisation:

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

(posté principalement pour le diagramme de la collection)


35
2017-09-09 15:12



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

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

De cette façon, vous devez changer une seule ligne si vous décidez d'utiliser un TreeSet au lieu.

De plus, les méthodes qui fonctionnent sur les ensembles doivent spécifier les paramètres du type Set:

public static void print(Set<String> s)

alors la méthode peut être utilisée pour toutes les implémentations définies.

En théorie, nous devrions faire la même recommandation pour les listes liées, à savoir sauver Références LinkedList dans les variables de type List. Cependant, dans la bibliothèque Java, l'interface List est commune à la fois ArrayList et le LinkedList classe. En particulier, il a obtenu et défini des méthodes pour l'accès aléatoire, même si ces méthodes sont très inefficaces pour les listes liées.

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

Ceci est clairement une erreur de conception sérieuse dans la bibliothèque standard, et je ne peux pas recommander d'utiliser l'interface de liste pour cette raison.

Pour voir à quel point cette erreur est embarrassante, jetez un oeil à le code source de la binarySearch méthode de Collections classe. Cette méthode prend un Liste le paramètre, mais la recherche binaire n'a aucun sens pour une liste liée. Le code alors maladroitement essaie de découvrir si la liste est une liste liée, puis passe à une recherche linéaire!

le Set l'interface et le Map interface, sont bien conçus, et vous devriez les utiliser.


24
2017-08-21 11:46



J'utilise (2) si le code est le "propriétaire" de la liste. Ceci est par exemple vrai pour les variables locales uniquement. 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
2018-04-21 12:20



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


9
2018-02-17 07:55



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

Sérialisation!

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

De toute évidence, la plupart des gens n'ont pas besoin de sérialisation et oublient 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
2017-11-14 10:22



(3) Collection myCollection = new ArrayList ();

Je l'utilise typiquement. Et seulement Si j'ai besoin de méthodes List, j'utiliserai List. Idem avec ArrayList. Vous pouvez toujours passer à une interface plus "étroite", mais vous ne pouvez pas passer à plus "large".


8
2018-02-17 07:51



Quand vous écrivez List, vous dites en fait, que votre objet met en œuvre List interface uniquement, mais vous ne spécifiez pas à quelle classe appartient votre objet.

Quand 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 les documents Java:

Classe ArrayList - Implémentation de tableau redimensionnable List interface.

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

Array - objet conteneur contenant un nombre fixe de valeurs d'un seul type.


8
2017-12-05 17:14



Sur les deux suivants:

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

Le premier est généralement préféré. Comme vous utiliserez des méthodes de List interface seulement, il vous donne la liberté d'utiliser une autre implémentation de List par exemple. LinkedList dans le futur. Cela vous décoube donc d'une implémentation spécifique. Maintenant, il y a deux points à mentionner:

  1. Nous devons toujours programmer pour l'interface. Plus ici.
  2. Vous finirez presque toujours par utiliser ArrayList plus de LinkedList. Plus ici.

Je me demande si quelqu'un utilise (2)

Oui parfois (lire rarement). Lorsque nous avons besoin de méthodes qui font partie de la mise en œuvre de ArrayList mais ne fait pas partie de l'interface List. Par exemple ensureCapacity.

Aussi, à quelle fréquence (et puis-je avoir un exemple) est-ce que la situation   nécessite en fait d'utiliser (1) plus de (2)

Presque toujours, vous préférez l'option (1). Ceci est un modèle de conception classique dans OOP où vous essayez toujours de découpler votre code de l'implémentation spécifique et du programme à l'interface.


6
2017-07-03 08:40