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并发同步阻塞队列,不存储任何元素,可以选择公平模式和非公平模式