java - How is my implementation of an immutable stack? - Code Review Stack Exchange



java - How is my implementation of an immutable stack? - Code Review Stack Exchange

Writing an elegant immutable stack is difficult in Java, mainly due to the lack of multiple return values. But your code does look quite fine.

I am comparing your API with Scala's immutable stack. One interesting bit is that there the signature of push translates to:

public <T2 super T> ImStack<T2> push(T2 e)

which looks uncomfortable but makes a lot of sense too: Adding elements to a stack can widen the type but never narrow it down. Keeping the current type is a special case of this.

Your isEmpty method is unfortunately flawed. Assume I have created a stack of one element, like:

Object myObject = null;  ImStack<Object> stack = new ImStack<Object>(myObject, null);

Now stack.peek() is correctly null. On an empty stack, this should have created an exception, not returned a null.

One solution to solve this problem is to have isEmpty() always return false. We also create a private subclass¹ of ImStack called ImEmptyStack, where isEmpty() returns true, and peek() throws an exception. In your constructor, you create a new empty stack as tail when that argument would be null:

ImStack(T head, ImStack<? extends T> tail) {    this.head = head;    this.tail = tail != null ? tail : new ImEmptyStack<T>();  }

1: Actually, subclassing here leads to problems. The two classes should both be subtypes of a common interface. So basically, the Composite Pattern.

By overriding the size() in the ImEmptyStack, you can simplify that as well:

class ImStack<T> {    public int size() {      return 1 + tail.size();    }  }    class ImEmptyStack<T> extends ImStack<T> {    public int size() {      return 0;    }  }

Back to your isEmpty() method, code like this should never happen:

if ( head == null && tail == null)      return true;  else return false;

because it is equivalent to return(head == null && tail == null). Every time you write code like return foo() ? true : false, you know you can simplify that.

I'd like to sketch your reverse() method in a more functional manner, using an accumulator argument:

public ... reverse() {    return this.reverse(new ImEmptyStack<T>());  }    private ... reverse(ImStack<...> acc) {    return tail.reverse(acc.push(head));  }    // Empty stack:    private ... reverse(... acc) {    return acc;  }

This is as efficient as your solution, because your size call also traverses the whole stack recursively. The advantage of my solution is the simplicity. Of course, it is trivial to rewrite this to an iterative solution, because the above sketch is tail recursive:

reverse() {    ImStack<T> accumulator = new ImEmptyStack();    ImStack<T> current     = this;    while(!( current instanceof ImEmptyStack<T> )) {      accumulator = accumulator.push(current.peek());      current     = current.pop();    }    return accumulator;  }

Note that .pop() can never return null in my adaption of your code (an empty stack should throw an exception).


Something minor that I noticed is your inconsistent spacing around the argument lists of your methods:

ImStack ( T head, ImStack<T> tail)  // left outer and inner space  pop()  // no space  push( T e) // left inner space

This is no problem in itself, but it would be better to settle for one style.


Read full article from java - How is my implementation of an immutable stack? - Code Review Stack Exchange


No comments:

Post a Comment

Labels

Algorithm (219) Lucene (130) LeetCode (97) Database (36) Data Structure (33) text mining (28) Solr (27) java (27) Mathematical Algorithm (26) Difficult Algorithm (25) Logic Thinking (23) Puzzles (23) Bit Algorithms (22) Math (21) List (20) Dynamic Programming (19) Linux (19) Tree (18) Machine Learning (15) EPI (11) Queue (11) Smart Algorithm (11) Operating System (9) Java Basic (8) Recursive Algorithm (8) Stack (8) Eclipse (7) Scala (7) Tika (7) J2EE (6) Monitoring (6) Trie (6) Concurrency (5) Geometry Algorithm (5) Greedy Algorithm (5) Mahout (5) MySQL (5) xpost (5) C (4) Interview (4) Vi (4) regular expression (4) to-do (4) C++ (3) Chrome (3) Divide and Conquer (3) Graph Algorithm (3) Permutation (3) Powershell (3) Random (3) Segment Tree (3) UIMA (3) Union-Find (3) Video (3) Virtualization (3) Windows (3) XML (3) Advanced Data Structure (2) Android (2) Bash (2) Classic Algorithm (2) Debugging (2) Design Pattern (2) Google (2) Hadoop (2) Java Collections (2) Markov Chains (2) Probabilities (2) Shell (2) Site (2) Web Development (2) Workplace (2) angularjs (2) .Net (1) Amazon Interview (1) Android Studio (1) Array (1) Boilerpipe (1) Book Notes (1) ChromeOS (1) Chromebook (1) Codility (1) Desgin (1) Design (1) Divide and Conqure (1) GAE (1) Google Interview (1) Great Stuff (1) Hash (1) High Tech Companies (1) Improving (1) LifeTips (1) Maven (1) Network (1) Performance (1) Programming (1) Resources (1) Sampling (1) Sed (1) Smart Thinking (1) Sort (1) Spark (1) Stanford NLP (1) System Design (1) Trove (1) VIP (1) tools (1)

Popular Posts