Pourquoi devrais-je utiliser Deque sur Stack?


157

J'ai besoin d'une Stackstructure de données pour mon cas d'utilisation. Je devrais pouvoir pousser des éléments dans la structure de données et je souhaite uniquement récupérer le dernier élément de la pile. Le JavaDoc for Stack dit:

Un ensemble plus complet et cohérent d'opérations de pile LIFO est fourni par l'interface Deque et ses implémentations, qui doivent être utilisées de préférence à cette classe. Par exemple:

Deque<Integer> stack = new ArrayDeque<>();

Je ne veux certainement pas de comportement synchronisé ici car j'utiliserai cette structure de données locale à une méthode. En dehors de cela pourquoi je préfère Dequeplus Stackici?

PS: Le javadoc de Deque dit:

Les Deques peuvent également être utilisés comme piles LIFO (Last-In-First-Out). Cette interface doit être utilisée de préférence à l'ancienne classe Stack.


1
Il fournit plus de méthodes intégrées, ou plutôt «un ensemble plus complet et cohérent» d'entre elles, ce qui réduira la quantité de code que vous devez écrire si vous en tirez parti?

Réponses:


190

D'une part, c'est plus sensé en termes d'héritage. Le fait que cela se Stackprolonge Vectorest vraiment étrange, à mon avis. Au début de Java, l'héritage était surutilisé à l'OMI - Propertiesun autre exemple.

Pour moi, le mot crucial dans les documents que vous avez cités est cohérent . Dequeexpose un ensemble d'opérations qui consiste à pouvoir récupérer / ajouter / supprimer des éléments au début ou à la fin d'une collection, itérer, etc. - et c'est tout. Il n'y a délibérément aucun moyen d'accéder à un élément par position, ce qui Stackexpose parce que c'est une sous-classe de Vector.

Oh, et Stackn'a pas non plus d'interface, donc si vous savez que vous avez besoin d' Stackopérations, vous finissez par vous engager dans une classe concrète spécifique, ce qui n'est généralement pas une bonne idée.

Également comme indiqué dans les commentaires, Stacket Dequeont des ordres d'itération inversés:

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(new ArrayList<>(stack)); // prints 1, 2, 3


Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
System.out.println(new ArrayList<>(deque)); // prints 3, 2, 1

qui est également expliqué dans les JavaDocs pour Deque.iterator () :

Renvoie un itérateur sur les éléments de ce deque dans l'ordre approprié. Les éléments seront retournés dans l'ordre du premier (tête) au dernier (queue).


10
le javadoc d'ArrayDeque dit "Cette classe est susceptible d'être plus rapide que Stack lorsqu'elle est utilisée comme pile, et plus rapide que LinkedList lorsqu'elle est utilisée comme file d'attente." ..Comment puis-je spécifier si j'ai l'intention de l'utiliser comme pile ou comme file d'attente?
Geek

23
@ Geek: Vous ne le faites pas. Le fait est que si vous voulez un comportement de mise en file d'attente, vous pouvez l' utiliser LinkedList, mais ce ArrayDequeuesera (souvent) plus rapide. Si vous voulez un comportement de pile, vous pouvez utiliser Stackmais ArrayDequesera (souvent) plus rapide.
Jon Skeet

7
N'est-ce pas moins sensé en termes d'abstraction? Je veux dire, aucune des deux solutions n'est vraiment bonne en termes d'abstraction, car il y Stacka le problème d'exposition des représentants, mais si je veux une structure de données de pile, j'aimerais pouvoir appeler des méthodes comme push, pop et peek, et pas les choses doivent faire avec l'autre extrémité de la pile.
PeteyPabPro

4
@JonSkeet également l'itérateur de Stack est faux, car il itère de bas en haut au lieu de haut en bas. stackoverflow.com/questions/16992758/…
Pavel

1
@PeteyPabPro: Vous avez raison. L'utilisation de Dequeue comme pile permet toujours une utilisation non LIFO comme méthodes vectorielles héritées de Stack. Une explication du problème et une solution (avec encapsulation) se trouve ici: baddotrobot.com/blog/2013/01/10/stack-vs-deque
RICS

4

Voici mon interprétation de l'incohérence mentionnée dans la description de la classe Stack.

Si vous regardez les implémentations à usage général ici , vous verrez qu'il existe une approche cohérente de la mise en œuvre de l'ensemble, de la carte et de la liste.

  • Pour l'ensemble et la carte, nous avons 2 implémentations standard avec des cartes de hachage et des arbres. Le premier est le plus utilisé et le second est utilisé lorsque nous avons besoin d'une structure ordonnée (et il implémente également sa propre interface - SortedSet ou SortedMap).

  • Nous pouvons utiliser le style préféré de déclaration comme Set<String> set = new HashSet<String>();voir les raisons ici .

Mais la classe Stack: 1) n'a pas sa propre interface; 2) est une sous-classe de la classe Vector - qui est basée sur un tableau redimensionnable; Alors, où est la mise en œuvre de la liste chaînée de la pile?

Dans l'interface Deque, nous n'avons pas de tels problèmes, y compris deux implémentations (tableau redimensionnable - ArrayDeque; liste liée - LinkedList).


2

Une autre raison d'utiliser Dequeue sur Stack est que Dequeue a la possibilité d'utiliser des flux convertis en liste tout en conservant le concept LIFO appliqué alors que Stack ne le fait pas.

Stack<Integer> stack = new Stack<>();
Deque<Integer> deque = new ArrayDeque<>();

stack.push(1);//1 is the top
deque.push(1)//1 is the top
stack.push(2);//2 is the top
deque.push(2);//2 is the top

List<Integer> list1 = stack.stream().collect(Collectors.toList());//[1,2]

List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]

2

Voici quelques raisons pour lesquelles Deque est meilleur que Stack:

Conception orientée objet - Héritage, abstraction, classes et interfaces: Stack est une classe, Deque est une interface. Une seule classe peut être étendue, alors que n'importe quel nombre d'interfaces peut être implémenté par une seule classe en Java (héritage multiple de type). L'utilisation de l'interface Deque supprime la dépendance sur la classe Stack concrète et ses ancêtres et vous donne plus de flexibilité, par exemple la liberté d'étendre une classe différente ou d'échanger différentes implémentations de Deque (comme LinkedList, ArrayDeque).

Incohérence: Stack étend la classe Vector, qui vous permet d'accéder élément par index. Ceci est incompatible avec ce qu'une pile devrait réellement faire, c'est pourquoi l'interface Deque est préférée (elle ne permet pas de telles opérations) - ses opérations autorisées sont cohérentes avec ce qu'une structure de données FIFO ou LIFO devrait permettre.

Performances: La classe Vector que Stack étend est essentiellement la version "thread-safe" d'un ArrayList. Les synchronisations peuvent potentiellement entraîner une baisse significative des performances de votre application. En outre, l'extension d'autres classes avec des fonctionnalités inutiles (comme mentionné dans # 2) gonfle vos objets, ce qui peut coûter beaucoup de mémoire supplémentaire et de surcharge de performances.


-1

Pour moi, ce point spécifique manquait: Stack est Threadsafe car il est dérivé de Vector, alors que les implémentations les plus deque ne le sont pas, et donc plus rapide si vous ne l'utilisez que dans un seul thread.


grep "A part ça"
Pacerier

-1

La performance pourrait être une raison. Un algorithme que j'ai utilisé est passé de 7,6 minutes à 1,5 minutes en remplaçant simplement Stack par Deque.


grep "A part ça"
Pacerier

-6

Deque est utilisé dans une situation où vous souhaitez récupérer des éléments à la fois de la tête et de la queue. Si vous voulez une pile simple, il n'est pas nécessaire de faire un deque.


1
veuillez consulter le dernier paragraphe de la question. Le javadoc dit que Deques devrait être utilisé et je voulais savoir pourquoi?
Geek

2
Deque est préférable car vous ne pouvez pas accéder aux éléments du milieu. Il est double, donc peut être FILO pour FIFO.
Thufir

Je pense que ce qu'ils ont l'intention de dire, c'est que la classe prend en charge les opérations Stack en tant que méthodes explicites. Voir sa documentation et ses méthodes. Il a un support explicite pour l'implémentation d'un Stack.
Abhishek Nandgaonkar
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.