1.理解不可变性,对象一旦生成,内部状态不会发生变化,所以是线程安全的;
2.实现不可变队列ImmutableQueue<E>,利用两个不可变栈ImmutableStack<E>实现。
(一)不可变栈ImmutableStack<E>实现
import java.util.Collections;
import java.util.NoSuchElementException;
/**
* @author pizi
* The Stack class represents an immutable first-in-last-out(FILO) stack of objects.
* @param <E>
*
*/
public final class ImmutableStack<E> {
public final static ImmutableStack EMPTY_STACK = new ImmutableStack<>();
private final E front;
private final ImmutableStack<E> tail;
private final int size;
public ImmutableStack() {
front = null;
tail = null;
size = 0;
}
// not safe
public static final <E> ImmutableStack<E> emptyStack() {
return (ImmutableStack<E>)EMPTY_STACK;
}
private ImmutableStack(E f, ImmutableStack<E> t) {
front = f;
tail = t;
size = tail.size() + 1;
}
/**
*
* <pre>
* e.g.
* When this stack represents the stack(7, 1, 3, 3, 5, 1),
* this method returns 1 and this object still represents the stack(7, 1, 3, 3, 5, 1)
* </pre>
* If the stack is empty, throws java.util.NoSuchElementException.
* @return
* @throws java.util.NoSuchElementException
*/
public E peek() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return front;
}
/**
* Returns the stack that adds an item into the front of this stack without modifying this stack.
* <pre>
* e.g.
* When this stack represents the stack (2, 1, 2, 2, 6) and we push the value 4 into this stack,
* this method returns a new stack(2, 1, 2, 2, 6, 4)
* and this object still represents the stack(2, 1, 2, 2, 6).
* </pre>
* If the element e is null, throws IllegalArgumentException.
* @param e
* @return
* @throws IllegalArgumentException
*/
public ImmutableStack<E> push(E e) {
if (e == null) {
throw new IllegalArgumentException();
}
return new ImmutableStack<E>(e, this);
}
/**
* Returns the stack that removes the object at the front of this stack without modifying this stack.
* <pre>
* e.g.
* When this stack represents the stack(7, 1, 3, 3, 5, 1),
* this method returns a new stack(7, 1, 3, 3, 5)
* and this object still represents the stack(7, 1, 3, 3, 5, 1).
* </pre>
* If this stack is empty, throws java.util.NoSuchElementException.
* @return
* @throws java.util.NoSuchElementException
*/
public ImmutableStack<E> pop() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return tail;
}
/**
* reverse the stack elements to a new stack
*/
public ImmutableStack<E> reverse() {
if (isEmpty()) {
return new ImmutableStack<E>();
}
ImmutableStack<E> newStack = emptyStack();
//ImmutableStack<E> newStack = new ImmutableStack<E>();
ImmutableStack<E> currentStack = this;
while (!currentStack.isEmpty()) {
newStack = newStack.push(currentStack.peek());
currentStack = currentStack.pop();
}
/**
* ImmutableStack<E> r = emptyStack();
* for (ImmutableStack<E> f = this; !f.isEmpty(); f = f.pop()) {
* r = r.push(f.peek());
* }
*/
return newStack;
}
/**
* Returns the number of objects in this stack.
* @return
*/
public int size() {
return size;
}
/**
* <p>This implementation returns <tt>size() == 0</tt>.
*/
public boolean isEmpty() {
return size() == 0;
}
public String toString() {
if (isEmpty()) {
return "[]";
}
ImmutableStack<E> currentStack = this.reverse();
StringBuffer sb = new StringBuffer();
sb.append("[");
while (!currentStack.isEmpty()) {
sb.append(currentStack.peek() + (currentStack.size() > 1 ? ", " : ""));
currentStack = currentStack.pop();
}
return sb.append("]").toString();
}
}
(二)不可变队列ImmutableQueue<E>实现
import java.util.Collections;
import java.util.NoSuchElementException;
/**
* @author pizi
* The Queue class represents an immutable first-in-first-out(FIFO) queue of objects.
* @param <E>
*
*/
public final class ImmutableQueue<E> {
private final ImmutableStack<E> enterStack;
private final ImmutableStack<E> outStack;
public static final ImmutableQueue EMPTY_QUEUE = new ImmutableQueue<>();
/**
* requires default constructor
*/
public ImmutableQueue() {
// modify this constructor if necessary,but do not remove default constructor
enterStack = ImmutableStack.EMPTY_STACK;
outStack = ImmutableStack.EMPTY_STACK;
}
// add other constructors if necessary
private ImmutableQueue(ImmutableStack<E> eStack, ImmutableStack<E> oStack) {
enterStack = eStack;
outStack = oStack;
}
/**
* @SuppressWarnings("unchecked")
*/
public static final <E> ImmutableQueue<E> emptyQueue() {
return (ImmutableQueue<E>)EMPTY_QUEUE;
}
/*private static <E> E cast(Object o) {
return (E)o;
}*/
/**
* Returns the queue that adds an item into the tail of this queue without modifying this queue.
* <pre>
* e.g.
* When this queue represents the queue (2, 1, 2, 2 6) and we enqueue the value 4 into this queue,
* this method returns a new queue(2, 1, 2, 2, 6, 4)
* and this object still represents the queue(2, 1, 2, 2, 6).
* </pre>
* If the element e is null, throws IllegalArgumentException.
* @param e
* @return
* @throws IllegalArgumentException
*/
public ImmutableQueue<E> enqueue(E e) {
if (e == null) {
throw new IllegalArgumentException();
}
return new ImmutableQueue<E>(enterStack.push(e), outStack);
}
/**
* Returns the queue that removes the object at the head of this queue without modifying this queue.
* <pre>
* e.g.
* When this queue represents the queue(7, 1, 3, 3, 5, 1),
* this method returns a new queue(1, 3, 3, 5, 1)
* and this object still represents the queue(7, 1, 3, 3, 5, 1).
* </pre>
* If this queue is empty, throws java.util.NoSuchElementException.
* @return
* @throws java.util.NoSuchElementException
*/
public ImmutableQueue<E> dequeue() {
if (isEmpty()) {
throw new NoSuchElementException();
}
if (size() == 1) {
return emptyQueue();
//return new ImmutableQueue<E>();
}
// queue is not empty but outStack is empty and enterStack is not empty
if (outStack.isEmpty()) {
ImmutableStack<E> stack = enterStack.reverse();
return new ImmutableQueue<E>(ImmutableStack.EMPTY_STACK,stack.pop());
//return new ImmutableQueue<E>(new ImmutableStack<E>(), stack.pop());
}
// queue is not empty and outStack is not empty and enterStack may be empty or not
return new ImmutableQueue<E>(enterStack, outStack.pop());
}
/**
* Looks at the object which is the head of this queue without removing it from the queue.
* <pre>
* e.g.
* When this queue represents the queue(7, 1, 3, 3, 5, 1),
* this method returns 7 and this object still represents the queue(7, 1, 3, 3, 5, 1)
* </pre>
* If the queue is empty, throws java.util.NoSuchElementException.
* @return
* @throws java.util.NoSuchElementException
*/
public E peek() {
if (isEmpty()) {
throw new NoSuchElementException();
}
// queue is not empty but outStack is empty and enterStack is not empty
if (outStack.isEmpty()) {
ImmutableStack<E> stack = enterStack.reverse();
return stack.peek();
}
// outStack is not empty
return outStack.peek();
}
/**
* Returns the number of objects in this queue.
* @return
*/
public int size() {
return enterStack.size() + outStack.size();
}
/**
*
*/
public boolean isEmpty() {
return enterStack.isEmpty() &&
outStack.isEmpty();
}
public String toString() {
if (isEmpty()) {
return "[]";
}
StringBuffer sb = new StringBuffer();
sb.append("[");
// outStack is empty and enterStack is not empty
if (outStack.isEmpty() && !enterStack.isEmpty()) {
sb = toQueueString(sb, enterStack.reverse());
}
// outStack is not empty and enterStack is empty
if (!outStack.isEmpty() && enterStack.isEmpty()){
sb = toQueueString(sb, outStack);
}
// outStack is not empty and enterStack is not empty
if (!outStack.isEmpty() && !enterStack.isEmpty()) {
sb = toQueueString(sb, outStack);
sb.append(", ");
sb = toQueueString(sb, enterStack.reverse());
}
return sb.append("]").toString();
}
private StringBuffer toQueueString(StringBuffer sb, ImmutableStack<E> stack) {
while (!stack.isEmpty()) {
sb.append(stack.peek() + (stack.size() > 1 ? ", " : ""));
stack = stack.pop();
}
return sb;
}
}