Stack栈
数据的插入和删除只能在一个位置(末端)上进行,末端叫做栈顶,特征后进先出LIFO,实际上针对栈只有两种基本操作push进栈和pop出栈,基本实现有数组(顺序栈)和链表(链式栈)
顺序栈的实现
public class ArrayStack {// 可以使用泛型或者object类型
private String[] items;// 数组用于存储数据
private int count;// 用于统计栈中元素的个数
// 初始化
public ArrayStack(int capacity) {
items = new String[capacity];
count = 0;
}
// 进栈
public boolean push(String item) {
if (count == items.length) {
return false;// 存储空间不足,直接返回false
}
items[count++] = item;
return true;
}
// 出栈
public String pop() {
if (count == 0) {
return null;// 如果为空,则直接返回null
}
return items[--count];
}
}
如果需要使用一个支持动态扩容的顺序栈,则底层还需要实现一个支持动态扩容的数组
System.arraycopy(src,srcPos,dest,destPos,length);
Stack类
JDK中提供了一个堆栈类Stack,继承了Vector,提供了常见的push和pop操作
public class Stack<E> extends Vector<E>
已经不再建议使用,可以使用更完善、可靠性更强的LIFO操作由Deque接口和它的实现
Deque接口
public interface Deque<E> extends Queue<E>
Deque接口的常见数组实现为ArrayDeque
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < 5; i++) {
stack.push(i);
}
System.out.println("size:" + stack.size());
for (int i = 0; i < 5; i++) {
System.out.println(stack.pop());
}
System.out.println("size:" + stack.size());
队列Queue
队列是一种操作受限的线性表,在表的前端进行删除操作,在表的后端进行插入操作;两种基本操作:入队enqueue和出队dequeue。
特征:FIFO先进先出根据不同的实现方式可以分为顺序队列和链式队列
顺序队列
由于没有移动数据,所以数组的利用不充分,仅仅是模拟队列的原理
public class ArrayQueue{
private String[] items;
private int head = 0;//队列头部
private int tail = 0;//队列尾部
public ArrayQueue(){
items = new String[capacity];
}
public boolean enqueue(String item){
if(tail == items.length){
return false;//队列已满
}
items[tail++] = item;
return true;
}
public String dequeue(){
if(head == tail){//队列为空
return null;
}
return items[head++];
}
}
Queue接口
JDK中提供了Queue队列接口,表示一个FIFO的数据结构
public interface Queue<E> extends Collection<E>
add和offer新增数据。一个满的队列中加入新数据,多出的数据项就会被拒绝。add抛异常,而offer返回false
poll和remove从队列头部删除数据,当队列为空时remove同Collection接口定义,poll只是返回null
peek和element在队列头部查询元素,当队列为空时element抛出异常,而peek返回null
循环队列
public class Ci rcleQueue{
private String[] items;
private int head=0;
private int tail=0;
public Ci rclequeue(int capacity){
items=new String[capacity];
}
public boolean enqueue(String item){
//判断队列存储已经到达上限
if((tail+1)%items.length==head)
return false;
items[tail]=item;
//循环的体现,到达数组的末尾再从头开始
tail=(tail+1)%items.length;
return true;
}
public String dequeue(){
if(head==tail)
return null;
String res=items[head];
//获取头指针指向的数据
head=(head+1)%items.length;
return res;
}
}
常见的非阻塞队列
ArrayDeque数组实现的两倍自动扩容双端队列
Queue<Integer> queue = new ArrayDeque<>(4);
for(int i = 0; i < 5; i++)
queue.add(i);//addLast
for(int i = 0; i < 6; i++)
//System.out.println(queue.remove());//removeFirst NoSuchElementException
System.out.println(queue.poll());
queue.element()
//获取元素,但是不删除
ConcurrentLinkedQueue基于链表的并发队列
基于链表提供的实现,线程安全,并发访问不需要进行同步,高并发时收集关于队列大小的信息是很满的,因为需要遍历队列
Priority Queue优先级队列
实质上就是维护一个有序队列,加入队列的元素可以进行排序【Comparable接口或者Comparator接口】
阻塞队列
BlockingQueue接口针对插入、移除、获取元素提供了不同的方法用于应对不同的场景
- 抛出异常
- 返回特殊值,例如null或者true/false
- 阻塞操作
- 阻塞等待超时
public interface BlockingQueue<E> extends Queue<E>
抛出异常 | 返回特殊值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,TimeUnit) |
删除 | remove() | poll() | take() | poll(time,TimeUnit) |
获取 | element() | peek() | - | - |
具体实现
ArrayBlockingQueue基于数组的并发阻塞队列,底层是数组,有界队列
LinkedBlockingQueue基于链表的FIFO阻塞队列,底层是链表,可以当作有界或者无界队列
PriorityBlockingQueue带优先级的无界阻塞队列,基于数组的二叉树
SynchronousQueue并发同步阻塞队列,不存储任何元素,可以选择公平模式和非公平模式