实现原理
栈的特性是先进后出,队列的特性是先进先出。那么,我们使用两个栈,对同一个元素进行先进后出两次栈就形成了先进先出的顺序。
即一个元素需要入栈两次,才能被取出来。
我们将put,get定义为存放元素,与取元素。
使用命名为in,out的栈,in代表往队列里面put元素第一次入栈是进入in,out代表get元素是从out这个栈里取。
第一次,get时out为空,需要从in中取元素,以形成正序取。
其内部实现细节如下:
put(){
in.put;
}
get(){
//如果out为空,则从in里面取出所有第一次入栈元素放入out栈,形成最初的进栈顺序,这样底层的就在最上面。
in.all.get -> out
//然后 从out get
}
实现代码
public class TwoStackQueue<T> {
private Stack<T> in = new Stack<T>();
private Stack<T> out = new Stack<T>();
public void put(T t) {
in.push(t);
}
public T get() {
if (out.isEmpty()) {
if (in.isEmpty()) return null;
while (!in.isEmpty()) {
out.push(in.pop());
}
return out.pop();
} else {
return out.pop();
}
}
public boolean isEmpty() {
return (in.isEmpty() && out.isEmpty()) ? true : false;
}
public int size() {
return out.size() + in.size();
}
}
存在问题
若a还没取,c已进栈时,如何控制?很明显这是一个线程安全问题。
在out取in中的元素的时候我们需要对 in加锁,阻止他存放新元素进去。
而对于out,我们是在out为空的时候才取in中元素所以一般不会出现线程安全问题。
以下是简易版本实现:
public class TwoStackQueue<T> {
private Stack<T> in = new Stack<T>();
private Stack<T> out = new Stack<T>();
private ReentrantLock lock_in = new ReentrantLock();
public static void main(String[] args) {
TwoStackQueue<String> queue = new TwoStackQueue<>();
ExecutorService threadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int _i = i;
threadPool.execute(() -> {
queue.put(Thread.currentThread().getName() + " " + _i);
System.out.println(Thread.currentThread().getName() + "取到了 " + queue.get());
});
}
}
public void put(T t) {
lock_in.lock();
try {
in.push(t);
} finally {
lock_in.unlock();
}
}
public T get() {
try {
//只有当out为空的时候才会去in中取元素,这个时候可能产生线程安全问题
if (out.isEmpty()) {
if (in.isEmpty()) return null;
lock_in.lock();
try {
while (!in.isEmpty()) {
//在取in中元素的时候锁住in
out.push(in.pop());
}
} finally {
lock_in.unlock();
}
return out.pop();
} else {
return out.pop();
}
} finally {
}
}
public boolean isEmpty() {
return (in.isEmpty() && out.isEmpty()) ? true : false;
}
public int size() {
return out.size() + in.size();
}
}