Java队列实现与常用使用方法介绍
- 说明
- 使用数组手动实现队列功能
- 使用单向链表手动实现队列功能
- Java中ArrayBlockingQueue和ArrayQueue和LinkedBlockingQueue使用
- ArrayQueue使用方法如下:
- ArrayBlockingQueue常用使用方法如下:
- LinkedBlockingQueue常用使用方法如下:
- ArrayBlockingQueue和LinkedBlockingQueue区别
说明
这里记录下使用数组和链表手动实现队列功能,并使用java里面现成的队列方法。
使用数组手动实现队列功能
package com.example.deesign_patterns.test;
//java使用数组简单实现队列
public class ArrayQueue {
private int[] queue; // 内部数组
private int front; // 队列头部指针
private int rear; // 队列尾部指针
private int size; // 队列当前元素个数
private int capacity; // 队列容量
//构造方法,初始化的时候必须要传队列容量值
public ArrayQueue(int capacity) {
this.capacity = capacity;
queue = new int[capacity];
front = 0;
rear = -1;
size = 0;
}
//判断是否是空队列
public boolean isEmpty() {
return size == 0;
}
//判断队列是否已满
public boolean isFull() {
return size == capacity;
}
//队列元素个数
public int size() {
return size;
}
//元素入队列
public void add(int value) {
if (isFull()) {
System.out.println("队列已经满了,无法添加队列。");
return;
}
rear = (rear + 1) % capacity; // 循环队列,计算新的尾部位置
queue[rear] = value;//在数组尾部添加元素
size++;//如果添加成功,则队列元素数量加1
System.out.println("入队列元素值: " + value);
}
//头部元素出队列,重新计算头部位置,数组索引往后移了一位,相当于删除头元素
public int pull() {
if (isEmpty()) {
System.out.println("队列是空的,无法出队列。");
return -1;
}
int value = queue[front];
front = (front + 1) % capacity; // 循环队列,计算新的头部位置
size--;//如果出队列成功,则队列元素数量减1
System.out.println("出队列元素值: " + value);
return value;
}
//获取队列里面的首元素,不会删除元素
public int peek() {
if (isEmpty()) {
System.out.println("队列是空的!");
return -1;
}
return queue[front];
}
//显示队列元素
public void display() {
if (isEmpty()) {
System.out.println("队列是空的!");
return;
}
System.out.print("队列当前元素值: ");
int index = front;
for (int i = 0; i < size; i++) {
System.out.print(queue[index] + " ");
index = (index + 1) % capacity; // 循环遍历队列
}
System.out.println();
}
//测试类
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(5);
queue.add(10);
queue.add(20);
queue.add(30);
queue.display(); // Queue: 10 20 30
queue.pull();
queue.display(); // Queue: 20 30
queue.add(40);
queue.add(50);
queue.display(); // Queue: 20 30 40 50
queue.pull();
queue.pull();
queue.display(); // Queue: 40 50
System.out.println("当前队列首元素为:"+queue.peek());//Queue: 40
}
}
执行结果如下:
使用单向链表手动实现队列功能
单向链表类
package com.example.deesign_patterns.test;
//单向链表类
public class Node<T> {
public T data;
public Node next;
//无参构造方法
public Node() {}
//有参构造方法
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
队列需要实现的功能接口(也可以在链表队列类里面直接写方法):
package com.example.deesign_patterns.test;
/**
* 队列功能接口
* 队列是一种先进先出的线性表
* 只能在表的一端进行插入,另一段进行删除
* 允许插入的一端叫队尾,允许删除的一端叫队头()
*/
public interface IQueue<T> {
/**
* 初始化队列 构造一个空队列
*/
IQueue InitQueue();
/**
* 销毁队列
*/
IQueue DestroyQueue();
/**
* 清空队列
*/
IQueue ClearQueue();
/**
* 队列判空
*/
Boolean isEmpty();
/**
* 返回队列长度
*/
Integer QueueLength();
/**
* 返回队列头元素
*/
T GetHead();
/**
* 插入队尾元素
*/
Boolean EnQueue(T e);
/**
* 删除队头元素 即出队
*/
T DeQueue();
/**
* 打印队列元素
*/
void printQueue();
}
链表队列类:
package com.example.deesign_patterns.test;
//java使用链表简单实现队列
public class LinkedQueue<T> implements IQueue<T> {
private Node<T> front;//队头指针
private Node<T> rear;//队尾指针
private int size;//队列长度
//无参构造方法
public LinkedQueue() {
front = null;
rear = null;
size = 0;
}
//初始化队列方法
@Override
public IQueue InitQueue() {
front = null;
rear = null;
size = 0;
return this;
}
//销毁队列方法
@Override
public IQueue DestroyQueue() {
//销毁
front = null;
rear = null;
size = 0;
return this;
}
//清空队列方法
@Override
public IQueue ClearQueue() {
front = null;
rear = null;
size = 0;
return this;
}
//判断队列是否为空
@Override
public Boolean isEmpty() {
return size == 0;
}
//判断队列长度
@Override
public Integer QueueLength() {
return size;
}
//获取队列里面的首元素
@Override
public T GetHead() {
if (isEmpty()) {
return null;
}
return front.getData();
}
//将元素入队
@Override
public Boolean EnQueue(T e) {
Node<T> newNode = new Node<>(e, null);
if (isEmpty()) {
front = newNode;
} else {
rear.setNext(newNode);
}
rear = newNode;
size++;
return true;
}
//将元素出队
@Override
public T DeQueue() {
if (isEmpty()) {
return null;
}
T data = front.getData();
front = front.getNext();
size--;
if (isEmpty()) {
rear = null;
}
return data;
}
@Override
public void printQueue() {
for (Node current = front;current != null;current = current.next){
System.out.print(current.data+" ");
}
System.out.println();
}
//测试类
public static void main(String[] args) {
IQueue<Integer> queue = new LinkedQueue<>();
queue.InitQueue();
queue.EnQueue(1);
queue.EnQueue(2);
queue.EnQueue(3);
System.out.println("队列长度: " + queue.QueueLength());
queue.printQueue();//打印队列值
System.out.println("队头元素: " + queue.GetHead());
System.out.println("出队元素: " + queue.DeQueue());
System.out.println("出队元素: " + queue.DeQueue());
System.out.println("出队元素: " + queue.DeQueue());
queue.printQueue();//打印队列值
System.out.println("队列长度: " + queue.QueueLength());
System.out.println("队列是否为空: " + queue.isEmpty());
}
}
Java中ArrayBlockingQueue和ArrayQueue和LinkedBlockingQueue使用
在Java中,ArrayBlockingQueue和ArrayQueue是两种不同的队列实现。它们之间有以下区别:
- 数据结构不同:ArrayBlockingQueue是基于数组的有界阻塞队列,它的容量是固定的。而ArrayQueue是基于数组的无界队列,它的容量可以动态增长。
- 插入和删除操作的不同:ArrayBlockingQueue的插入和删除操作是线程安全的,并且支持阻塞操作。当队列已满时,插入操作将会被阻塞,直到有空间可用;当队列为空时,删除操作将会被阻塞,直到有元素可用。而ArrayQueue的插入和删除操作不是线程安全的,需要自行处理同步。
- 性能方面的不同:由于ArrayBlockingQueue支持阻塞操作,它在多线程环境下具有更好的线程安全性。然而,由于需要处理同步和阻塞,它的性能相对较低。相比之下,ArrayQueue在单线程环境下的性能更好,不需要处理同步和阻塞。
ArrayQueue使用方法如下:
package com.example.deesign_patterns.test;
import com.sun.jmx.remote.internal.ArrayQueue;
public class Test3Main {
public static void main(String[] args) {
//数组队列使用,给数组一个大小,当数组里面的元素数量超过5,就会报错,可以通过resize()方法扩容
ArrayQueue arrayQueue=new ArrayQueue<Integer>(5);
arrayQueue.add(1);//入队列
arrayQueue.add(2);
arrayQueue.add(3);
arrayQueue.add(4);
arrayQueue.add(5);
System.out.println("当前队列元素:"+arrayQueue);
System.out.println("当前队列大小:"+arrayQueue.size());
System.out.println("获取当前队列索引为1的元素:"+arrayQueue.get(1));
//重新设置队列大小,相当于扩容,扩容后大小由原来的5变为6,值必须比原来大
arrayQueue.resize(6);
arrayQueue.add(6);
System.out.println("当前队列元素:"+arrayQueue);
System.out.println("当前队列大小:"+arrayQueue.size());
//只能删除队列头元素,必须是索引0,不为0报错,相当于出队列
arrayQueue.remove(0);
System.out.println("当前队列元素:"+arrayQueue);
}
}
ArrayBlockingQueue常用使用方法如下:
在Java中,ArrayBlockingQueue是一个具有固定容量的阻塞队列。它的实现是基于数组的,它实现了BlockingQueue接口,提供了一组方法用于操作队列中的元素。
- add(element):向队列尾部添加一个元素,如果队列已满,则IllegalStateExceptio异常。
- offer(element):向队列尾部添加一个元素,如果队列已满,则返回false。
- put(element):向队列尾部添加一个元素,如果队列已满,则会一直阻塞直到队列有空闲位置。
- poll():从队列头部移除并返回一个元素,如果队列为空,则返回null。
- take():从队列头部移除并返回一个元素,如果队列为空,则会一直阻塞直到队列有元素可取。
- peek():获取队列头部的元素,但不会移除该元素。
- size():返回队列中当前元素的数量。
- remainingCapacity():返回队列中剩余的可用容量。
- isEmpty(): 判断队列是否为空,如果队列为空则返回true,否则返回false。
package com.example.deesign_patterns.test;
import java.util.concurrent.ArrayBlockingQueue;
public class Test3Main {
public static void main(String[] args) {
//数组队列使用,必须给数组一个大小,当数组里面的元素数量超过5,就会报错,不能扩容
ArrayBlockingQueue queue=new ArrayBlockingQueue(5);
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
queue.add(5);
System.out.println("当前队列元素:"+queue);
System.out.println("当前队列大小:"+queue.size());
System.out.println("队列已满返回boolean值为:"+queue.offer(6));
System.out.println("获取队列头部元素:"+queue.peek());
//出队列
queue.poll();
System.out.println("当前队列元素:"+queue);
System.out.println("当前队列可用容量:"+ queue.remainingCapacity());
}
}
LinkedBlockingQueue常用使用方法如下:
LinkedBlockingQueue是一个线程安全的队列,它的实现是基于链表的。LinkedBlockingQueue的容量可以是无限的。
常用使用方法和ArrayBlockingQueue队列方法一样
package com.example.deesign_patterns.test;
import java.util.concurrent.LinkedBlockingQueue;
public class Test3Main {
public static void main(String[] args) {
//链表队列使用
//如果没有给链表一个大小,默认值是Integer.MAX_VALUE
//LinkedBlockingQueue queue=new LinkedBlockingQueue();
//如果给链表一个大小,当链表里面的元素数量超过5,就会报错,不能扩容
LinkedBlockingQueue queue=new LinkedBlockingQueue(5);
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
queue.add(5);
System.out.println("当前队列元素:"+queue);
System.out.println("当前队列大小:"+queue.size());
System.out.println("队列已满返回boolean值为:"+queue.offer(6));
System.out.println("获取队列头部元素:"+queue.peek());
//出队列
queue.poll();
System.out.println("当前队列元素:"+queue);
System.out.println("当前队列可用容量:"+ queue.remainingCapacity());
}
}
ArrayBlockingQueue和LinkedBlockingQueue区别
Java中的ArrayBlockingQueue和LinkedBlockingQueue都是实现了BlockingQueue接口,用于实现多线程环境下的阻塞队列。它们之间的区别主要有以下几个方面:
- 数据结构:ArrayBlockingQueue使用数组作为底层数据结构,而LinkedBlockingQueue使用链表作为底层数据结构。这导致了它们在插入和删除操作上的性能特点不同。
- 队列大小:ArrayBlockingQueue有一个固定的容量,需要在创建时指定大小,而LinkedBlockingQueue的容量是可选的,默认为Integer.MAX_VALUE。
- 线程阻塞:ArrayBlockingQueue在插入和删除元素时,如果队列已满或为空,操作线程会被阻塞,直到有可用的空间或元素可供操作。而LinkedBlockingQueue在插入和删除元素时,如果队列已满或为空,操作线程可以选择被阻塞或立即返回。
- 迭代性能:由于LinkedBlockingQueue使用链表作为底层数据结构,迭代操作的性能通常比ArrayBlockingQueue更好。
- 吞吐量:在高并发场景下,LinkedBlockingQueue通常比ArrayBlockingQueue具有更好的吞吐量,因为它能够更好地支持并发的插入和删除操作。
综上所述,ArrayBlockingQueue适用于有固定容量要求的场景,操作线程需要在队列满或空时被阻塞;而LinkedBlockingQueue适用于没有固定容量要求的场景,可以更好地支持高并发的插入和删除操作。