Liste Type de rapport 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) soit généralement utilisé dans une application quel que soit le besoin (moi-même j'utilise toujours cela).

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

De plus, à quelle fréquence (et puis-je obtenir 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é le codage des interfaces et les meilleures pratiques etc.)

Author: nazar_art, 2010-02-17

15 answers

Presque toujours le premier est préféré au second. Le premier a l'avantage que l'implémentation du {[0] } peut changer (en a 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 pourriez avoir utilisé ArrayList méthodes spécifiques.

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

 378
Author: kgiannakakis, 2014-05-21 00:43:14

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

Oui. Mais rarement pour une raison saine (OMI).

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

  • Les méthodes utilitaires comme Collections.singletonList(...) ou Arrays.asList(...) ne renvoient pas un ArrayList.

  • Les méthodes de l'API List ne garantissent pas de renvoyer une liste du même type.

Par exemple de quelqu'un qui se fait brûler, dans https://stackoverflow.com/a/1481123/139985 l'affiche a eu des problèmes avec le "découpage" car ArrayList.sublist(...) ne renvoie pas un ArrayList... et il avait conçu son code pour utiliser ArrayList comme 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 selon lequel vous devez savoir comment le List se comporte est largement traité en utilisant l'interface de marqueur RandomAccess. Oui, c'est un peu maladroit, mais l'alternative est pire.

De plus, à quelle fréquence la situation nécessite-t-elle réellement d'utiliser (1) sur (2) (c'est-à-dire où (2) ne suffirait pas..mis à part le "codage aux interfaces" et les meilleures pratiques, etc.)

La partie "combien de fois" de la question est objectivement sans réponse.

(et puis-je obtenir un exemple)

Occasionnellement, 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 de ArrayList qui déclare la méthode comme étant public.)

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

(Il est théoriquement possible que vous obteniez une légère amélioration des performances ... sous certaines circonstances ... sur certaines plates-formes ... mais à moins que vous n'ayez vraiment besoin de ce dernier 0.05%, cela ne vaut pas la peine de le faire. Ce n'est pas une bonne raison, de l'OMI.)


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 valide. 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 l'appelez avec une liste qui n'en Å“uvre RandomAccess, vous obtiendrez une erreur de compilation.

Vous pouvez également tester dynamiquement ... en utilisant instanceof... si la saisie statique est trop maladroite. Et vous pouvez même écrire votre code pour utiliser différents algorithmes (dynamiquement) selon qu'une liste prend ou non en charge l'accès aléatoire.

Notez que ArrayList n'est pas la seule classe list qui implémente RandomAccess. D'autres incluent 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 également ce problème. (Dans la mesure où il est ré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.)

 99
Author: Stephen C, 2018-09-06 01:30:52

Par exemple, vous pouvez 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 performances.

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:

entrez la description de l'image ici

(publié principalement pour le diagramme de collection)

 35
Author: 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 ne devez changer qu'une seule ligne si vous décidez d'utiliser un TreeSet à la place.

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

public static void print(Set<String> s)

, Puis la méthode peut être utilisée pour définir tous les implémentations.

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

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

C'est clairement une conception sérieuse erreur 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 la Collections classe. Cette méthode prend un Paramètre de liste, mais la recherche binaire n'a aucun sens pour une liste chaînée. Le code alors maladroitement essaie de découvrir si la liste est une liste chaînée, puis passe à une recherche linéaire!

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

 24
Author: 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 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
Author: mazatwork, 2013-04-21 12:20:37

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

 9
Author: True Soft, 2010-02-17 07:55:27

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

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

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

Regardez les documents Java:

Classe ArrayList - Redimensionnable-tableau de mise en Å“uvre de la List interface.

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

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

 9
Author: wzbozon, 2013-12-05 17:14:10

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 vous voulez pour contenir une liste, vous devez déclarer le terrain, de béton et serializable type ArrayList, car la List interface ne s'étend pas java.io.Serializable

Évidemment, 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
Author: raudi, 2016-11-14 11:20:47

(3) de la Collection myCollection = new ArrayList();

J'utilise ceci typiquement. Et seulement si j'ai besoin de méthodes List, j'utiliserai List. Même chose avec ArrayList. Vous pouvez toujours passer à une interface plus "étroite", mais vous ne pouvez pas passer à plus"large".

 8
Author: dart, 2010-02-17 07:51:38

Parmi les deux suivants:

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

First est généralement préféré. Comme vous utiliserez uniquement des méthodes de l'interface List, cela vous offre la liberté d'utiliser une autre implémentation de List, par exemple LinkedList à l'avenir. Il vous découple donc d'une implémentation spécifique. Maintenant, il y a deux points qui méritent d'être mentionnés:

  1. Nous devrions toujours programmer pour l'interface. Plus ici.
  2. Vous finirez presque toujours par utiliser ArrayList sur 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 l'implémentation de ArrayList mais ne font pas partie de l'interface List. Par exemple ensureCapacity.

Aussi, à quelle fréquence (et puis-je obtenir un exemple) la situation nécessite réellement l'utilisation de (1) sur (2)

Vous préférez presque toujours l'option (1). Ceci est un modèle de conception classique en POO où vous essayez toujours de découplez votre code de l'implémentation et du programme spécifiques à l'interface.

 6
Author: i_am_zero, 2017-05-23 11:33:24

Le seul cas dont je sache où (2) peut être meilleur est lors de l'utilisation de GWT, car cela réduit l'empreinte de l'application (ce n'est pas mon idée, mais l'équipe de Google web toolkit le dit). Mais pour java régulier, l'exécution dans la JVM (1) est probablement toujours meilleure.

 2
Author: Hans Westerbeek, 2010-02-17 07:54:29

La liste est un interface.It n'a pas de méthodes. Lorsque vous appelez la 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 implémente l'interface de liste .

 2
Author: Xar-e-ahmer Khan, 2015-10-27 11:50:44

Quelqu'un a encore demandé ceci (duplicate) 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 nous utilisons une visionneuse de bytecode (j'ai utilisé http://asm.ow2.org/eclipse/index.html ) nous voyons ce qui suit (uniquement l'initialisation et l'affectation de la liste) pour notre extrait de liste :

   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 de 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 de list finit par appeler INVOKEINTERFACE alors que accédez à la liste appels INVOKEVIRTUAL. Accoding à la référence du plugin Bycode Outline,

Invokeinterface est utilisé pour appeler une méthode déclarée dans un Java interface

Tout en invokevirtual

Appelle toutes les méthodes sauf les méthodes d'interface (qui utilisent invokeinterface), les méthodes statiques (qui utilisent invokestatic), et cas particuliers traités par invokespecial.

En résumé, invokevirtual affiche objectref hors de la pile tandis que pour invokeinterface

L'interpréteur affiche ' n 'éléments de la pile d'opérandes, où' n ' est un non signé de 8 bits 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 la façon dont chaque façon récupère objectref.

 2
Author: toxicafunk, 2016-12-23 11:06:00

Je dirais que 1 est préféré, à moins que

  • 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 ArrayList dans un appel de méthode qui nécessite ArrayList, éventuellement pour des caractéristiques de comportement ou de performance optionnelles

Je suppose que dans 99% des cas, vous pouvez vous en sortir avec List, ce qui est préférable.

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

List l'interface ont plusieurs classes différentes - ArrayList et LinkedList. LinkedList est utilisé 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 ne pouvez l'utiliser qu'avec ArrayList, pas LinkedList, mais vous pouvez autoriser l'utilisation de l'une des classes List sur d'autres endroits où sa méthode utilise, c'est juste votre choix, donc l'utilisation d'une interface peut le permettre:

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

Dans les arguments de cette méthode, vous pouvez utiliser l'une des classes List que vous souhaitez utiliser:

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

list.add ("string");

myMethod (list);

CONCLUSION:

Utilisez les interfaces partout lorsque cela est possible, ne vous limitez pas ou ne limitez pas les autres à utiliser différentes méthodes qu'ils souhaitent utiliser.

 0
Author: Acuna, 2018-06-15 21:43:29