javajava-8java-streamjava-collections-api

Stack using the Java 8 collection streaming API


I have a method which generates an object each time I execute it, and I need to reverse the order with which I am getting them. So I thought the natural way to do it would be a Stack, since it is LIFO.

However, the Java Stack does not seem to play well with the new Java 8 streaming API.

If I do this:

   Stack<String> stack = new Stack<String>();
   stack.push("A");
   stack.push("B");
   stack.push("C");

   List<String> list = stack.stream().collect(Collectors.toList());
   System.out.println("Collected: " + list);

The output I get is:

Collected: [A, B, C]

Why isn't it outputing them in the expected LIFO order to the stream? Is this the right way to flush out all the items from the stack to a list in the right (LIFO) order?


Solution

  • As already mentioned in the comments, we have well tested Deque interface which should be preferred.

    But I will you give the reason why Stack shouldn't be used.

    At first, the Java Doc. of the Stack says itself:

    A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:

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

    See JavaDoc.

    So what it is the problem with the Stack class.

    Like Martin Fowler already mentioned in his book Refactoring: Improving the Design of Existing Code at the refactoring method Replace Inheritance with Delegation, a Stack shouldn't inherit from a Vector.

    One of the classic examples of inappropriate inheritance is making a stack a subclass of vector. Java 1.1 does this in its utilities (naughty boys!) [6, p. 288]

    Instead, they should have used delegation like in the picture below, which is also from the book.

    See also here: Replace Inheritance with Delegation

    Replace Inheritance with Delegation

    So but why is this a problem:

    Because the Stack has only 5 Methods:

    1. pop
    2. push
    3. isEmpty
    4. search
    5. size

    size() and isEmpty() are inherited from the Vector class and the other methods from the Vector are not used. But through the inheritance, other methods are forwarded to the Stack class which makes no sense.

    And Fowler says to this problem:

    You can live with the situation and use convention to say that although it is a subclass, it's using only part of the superclass function. But that results in code that says one thing when your intention is something else—a confusion you should remove.

    This hurts the Interface Segregation Principle

    which says:

    CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE.


    You can check out the source code of the Vector and Stack class and you will see that the Stack class inherit the spliterator method and the VectorSpliterator innerClass from the Vector class.

    This method is used by the Collection interface to impl. the default version of stream method:

    default Stream<E> stream() {
      return StreamSupport.stream(spliterator(), false);
    }
    

    So avoid simply the usage of the Vector and Stack class.

    [6] Refactoring: Improving the Design of Existing Code Fowler, Martin year 1997