1. Java队列(Queue)接口深度解析
队列(Queue)作为Java集合框架中最重要的数据结构之一,其FIFO(先进先出)特性在各类应用场景中发挥着关键作用。不同于直接学习具体实现类,理解Queue接口本身的设计哲学才能真正掌握其精髓。
1.1 队列的核心特性与应用场景
队列本质上是一种受限的线性表,只允许在队尾(rear)进行插入操作(enqueue),在队头(front)进行删除操作(dequeue)。这种特性使其天然适合以下场景:
- 任务调度系统:操作系统中的进程调度、线程池任务管理
- 消息中间件:Kafka、RabbitMQ等消息队列的基础模型
- 算法实现:广度优先搜索(BFS)的标准实现方式
- 缓冲机制:生产者和消费者模式中的缓冲队列
在Java集合框架中,Queue接口位于java.util包,继承自Collection接口,其核心实现类包括:
- LinkedList:基于链表的通用队列实现
- ArrayDeque:基于循环数组的高效双端队列
- PriorityQueue:支持优先级排序的堆实现
- BlockingQueue:线程安全的阻塞队列(位于并发包)
重要提示:Queue本身是接口,不能直接实例化,必须通过具体实现类创建对象。这是新手常见的误区之一。
1.2 队列的继承体系与设计哲学
Java集合框架中Queue的继承关系如下:
code复制Collection
│
├── List
│
├── Set
│
└── Queue
│
├── Deque
│
├── PriorityQueue
│
└── BlockingQueue
Queue接口的设计体现了Java集合框架的几个重要原则:
- 接口与实现分离:Queue定义行为规范,具体实现由子类完成
- 操作安全性:提供异常处理和返回值处理两套API
- 功能扩展性:通过Deque扩展为双端队列,通过BlockingQueue支持并发
这种设计使得队列既能保持核心的FIFO特性,又能灵活适应不同场景需求。理解这种设计哲学比单纯记忆方法签名更为重要。
2. Queue接口方法详解与对比
2.1 队列的两套API设计
Queue接口最显著的特点是提供了两套功能相同但处理方式不同的方法,这是Java集合框架的统一设计风格:
| 操作类型 | 异常处理版本 | 返回值处理版本 |
|---|---|---|
| 插入元素 | add(e) | offer(e) |
| 删除元素 | remove() | poll() |
| 查看元素 | element() | peek() |
这种设计的背后考量是:
- add/remove/element:遵循Collection接口的通用约定,操作失败时抛出异常
- offer/poll/peek:队列特有的安全操作方法,通过返回值表示操作状态
2.1.1 插入操作对比
add(E e)方法特点:
- 成功时返回true
- 队列容量满时抛出IllegalStateException
- 继承自Collection接口,是通用集合操作方法
java复制Queue<Integer> queue = new ArrayDeque<>(2);
queue.add(1); // 成功
queue.add(2); // 成功
queue.add(3); // 抛出IllegalStateException
offer(E e)方法特点:
- 成功时返回true
- 失败时返回false(如容量限制队列已满)
- 不会抛出异常,性能开销更小
- 队列特有的安全操作方法
java复制Queue<Integer> queue = new ArrayDeque<>(2);
System.out.println(queue.offer(1)); // true
System.out.println(queue.offer(2)); // true
System.out.println(queue.offer(3)); // false
实际开发建议:在明确知道队列有足够容量时使用add(),否则优先使用offer(),特别是在容量受限的队列实现中。
2.1.2 删除操作对比
remove()方法特点:
- 返回并移除队头元素
- 队列为空时抛出NoSuchElementException
- 是Collection接口的标准方法
java复制Queue<String> queue = new LinkedList<>();
queue.add("A");
String item = queue.remove(); // 返回"A"
queue.remove(); // 抛出NoSuchElementException
poll()方法特点:
- 返回并移除队头元素
- 队列为空时返回null
- 不会抛出异常
- 是队列特有的安全方法
java复制Queue<String> queue = new LinkedList<>();
queue.offer("B");
String item = queue.poll(); // 返回"B"
System.out.println(queue.poll()); // 输出null
2.1.3 查看操作对比
element()方法特点:
- 返回但不移除队头元素
- 队列为空时抛出NoSuchElementException
- 是Collection接口的标准方法
java复制Queue<Integer> queue = new PriorityQueue<>();
queue.add(5);
int head = queue.element(); // 返回5
queue.clear();
head = queue.element(); // 抛出NoSuchElementException
peek()方法特点:
- 返回但不移除队头元素
- 队列为空时返回null
- 不会抛出异常
- 是队列特有的安全方法
java复制Queue<Integer> queue = new PriorityQueue<>();
queue.offer(10);
Integer head = queue.peek(); // 返回10
queue.poll();
head = queue.peek(); // 返回null
2.2 方法选择的最佳实践
在实际开发中,方法的选择应基于以下原则:
- 明确操作环境:如果确定队列状态(非空/有空间),可以使用异常版本
- 性能考量:异常处理比条件判断开销大,高频操作推荐安全方法
- 代码可读性:使用能清晰表达意图的方法名
- 空值处理:考虑队列可能包含null值时避免使用poll()
推荐组合:
java复制// 标准使用模式
while(queue.peek() != null) {
process(queue.poll());
}
// 或者
while(!queue.isEmpty()) {
process(queue.remove());
}
3. Queue的高级特性与Collection方法
3.1 继承自Collection的方法
作为Collection的子接口,Queue自然继承了Collection的所有方法,这些方法在队列操作中同样重要:
| 方法 | 描述 | 时间复杂度(LinkedList) |
|---|---|---|
| size() | 返回队列元素数量 | O(1) |
| isEmpty() | 判断队列是否为空 | O(1) |
| contains(Object) | 判断是否包含指定元素 | O(n) |
| clear() | 清空队列 | O(n) |
| iterator() | 返回迭代器 | O(1)创建 |
使用示例:
java复制Queue<String> queue = new LinkedList<>();
queue.addAll(Arrays.asList("A","B","C"));
// 遍历队列
for(String item : queue) {
System.out.println(item); // 保持FIFO顺序
}
// 批量操作
queue.removeIf(s -> s.startsWith("A"));
注意:虽然Queue继承了Collection的remove(Object)方法,但它与remove()(无参数)有本质区别:
- remove():移除并返回队头元素(队列操作)
- remove(Object):移除指定元素(集合操作)
3.2 队列的顺序保证
Queue接口本身不强制要求遍历顺序与FIFO一致,但所有标准实现都保持这一特性:
java复制Queue<Integer> queue = new LinkedList<>();
queue.add(1);
queue.add(2);
queue.add(3);
// 迭代顺序保证1→2→3
queue.forEach(System.out::println);
// 但PriorityQueue例外,按优先级排序
Queue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder());
pq.add(1);
pq.add(3);
pq.add(2);
pq.forEach(System.out::println); // 输出3→2→1
3.3 队列的null值限制
大多数Queue实现不允许插入null值,原因在于:
- poll()和peek()使用null作为特殊返回值
- 避免语义混淆(是空队列还是包含null元素?)
- 并发环境下更安全
java复制Queue<String> queue = new ArrayDeque<>();
queue.add(null); // 抛出NullPointerException
// LinkedList理论上允许null但不推荐
Queue<String> linkedQueue = new LinkedList<>();
linkedQueue.add(null); // 允许但危险
4. 队列实现类选型指南
4.1 标准队列实现对比
| 实现类 | 底层结构 | 线程安全 | 允许null | 特性 |
|---|---|---|---|---|
| LinkedList | 双向链表 | 否 | 是 | 通用实现,支持List和Deque |
| ArrayDeque | 循环数组 | 否 | 否 | 高性能,内存紧凑 |
| PriorityQueue | 堆 | 否 | 否 | 按优先级出队 |
| ConcurrentLinkedQueue | 链表 | 是 | 否 | 无锁并发实现 |
| ArrayBlockingQueue | 数组 | 是 | 否 | 固定容量,阻塞操作 |
4.2 选型决策要点
-
基本需求:
- 需要简单FIFO:LinkedList或ArrayDeque
- 需要优先级:PriorityQueue
- 需要并发:java.util.concurrent中的实现
-
性能考量:
- 高频插入/删除:ArrayDeque(O(1)时间)
- 随机访问:LinkedList(仍需要遍历)
- 内存敏感:ArrayDeque(连续内存)
-
特殊场景:
- 线程间通信:BlockingQueue
- 延迟任务:DelayQueue
- 无界队列:LinkedBlockingQueue
java复制// 典型使用场景示例
// 1. 高吞吐量场景
Queue<LogEvent> eventQueue = new ArrayDeque<>(1000);
// 2. 任务调度系统
Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
// 3. 优先级处理
Queue<Patient> emergencyQueue = new PriorityQueue<>(
Comparator.comparing(Patient::getSeverity).reversed()
);
5. 队列应用实践与陷阱规避
5.1 广度优先搜索(BFS)实现
队列是BFS算法的核心数据结构,典型实现模式:
java复制public void bfs(Node start) {
Queue<Node> queue = new LinkedList<>();
Set<Node> visited = new HashSet<>();
queue.offer(start);
visited.add(start);
while(!queue.isEmpty()) {
Node current = queue.poll();
process(current);
for(Node neighbor : current.getNeighbors()) {
if(!visited.contains(neighbor)) {
visited.add(neighbor);
queue.offer(neighbor);
}
}
}
}
5.2 生产者-消费者模式
队列作为缓冲区的经典应用:
java复制BlockingQueue<Item> queue = new LinkedBlockingQueue<>(100);
// 生产者线程
new Thread(() -> {
while(true) {
Item item = produceItem();
try {
queue.put(item); // 阻塞直到空间可用
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
// 消费者线程
new Thread(() -> {
while(true) {
try {
Item item = queue.take(); // 阻塞直到元素可用
consumeItem(item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
5.3 常见陷阱与解决方案
-
空队列操作:
- 错误:直接调用remove()或element()
- 正确:先检查isEmpty()或使用poll()/peek()
-
容量限制忽视:
- 错误:假设队列总是可以add()
- 正确:检查offer()返回值或使用无界队列
-
并发环境下的非线程安全队列:
- 错误:在多线程中共享非并发队列
- 正确:使用ConcurrentLinkedQueue或BlockingQueue
-
null值混淆:
- 错误:依赖poll()返回null判断队列状态,同时允许存储null
- 正确:禁止null值或使用特殊标记对象
-
迭代器并发修改:
- 错误:在迭代过程中修改队列
- 正确:先复制数据或使用并发集合
java复制// 错误示例
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
// 在迭代中修改会抛出ConcurrentModificationException
for(String s : queue) {
if("A".equals(s)) {
queue.remove(s);
}
}
// 正确做法1:使用迭代器的remove()
Iterator<String> it = queue.iterator();
while(it.hasNext()) {
if("A".equals(it.next())) {
it.remove();
}
}
// 正确做法2:使用临时集合
List<String> toRemove = queue.stream()
.filter("A"::equals)
.collect(Collectors.toList());
queue.removeAll(toRemove);
6. 性能优化与进阶技巧
6.1 队列初始化容量优化
对于基于数组的实现(如ArrayDeque),合理设置初始容量可避免频繁扩容:
java复制// 预估最大容量为1000
Queue<Event> eventQueue = new ArrayDeque<>(1000);
// 动态扩容仍然安全
for(int i=0; i<2000; i++) {
eventQueue.offer(new Event(i)); // 自动扩容,但影响性能
}
经验法则:如果能预估最大容量,设置初始大小为预估值的1.5倍,为临时峰值留出缓冲。
6.2 批量操作优化
利用Collection接口的批量操作方法提高性能:
java复制// 低效方式
for(Item item : itemList) {
queue.offer(item);
}
// 高效方式
queue.addAll(itemList);
6.3 内存布局优化
对于性能关键场景,考虑数据局部性:
java复制// 普通队列
Queue<Data> standardQueue = new LinkedList<>();
// 优化版本:数组存储+对象复用
class DataQueue {
private final Data[] buffer;
private int head, tail;
public DataQueue(int capacity) {
buffer = new Data[capacity];
Arrays.setAll(buffer, i -> new Data()); // 预分配对象
}
public void enqueue(Data input) {
buffer[tail++].copyFrom(input); // 复用对象
if(tail == buffer.length) tail = 0;
}
public Data dequeue() {
Data result = buffer[head];
if(++head == buffer.length) head = 0;
return result;
}
}
6.4 监控与调优指标
关键性能指标监控点:
- 队列长度波动:反映生产消费速率平衡
- 操作延迟:特别是poll()/offer()的耗时
- 扩容频率:对于基于数组的实现
- 争用情况:并发队列的锁竞争
java复制// 简单监控示例
class MonitoredQueue<T> implements Queue<T> {
private final Queue<T> delegate;
private final AtomicLong totalOps = new AtomicLong();
public MonitoredQueue(Queue<T> delegate) {
this.delegate = delegate;
}
@Override
public boolean offer(T e) {
boolean result = delegate.offer(e);
totalOps.incrementAndGet();
return result;
}
// 实现其他方法...
public long getTotalOperations() {
return totalOps.get();
}
}
7. 面试深度问题解析
7.1 Queue与Deque的区别
Queue(单端队列):
- 只能在队尾插入,队头删除
- 严格的FIFO语义
- 核心操作:offer(e)/poll()/peek()
Deque(双端队列):
- 允许在两端进行插入和删除
- 可作队列、栈或混合结构
- 扩展操作:
- addFirst(e)/addLast(e)
- removeFirst()/removeLast()
- getFirst()/getLast()
java复制Deque<String> deque = new ArrayDeque<>();
deque.offerLast("A"); // 队列操作
deque.offerFirst("B"); // 栈操作
String first = deque.pollFirst(); // "B"
7.2 阻塞队列与非阻塞队列
阻塞队列(BlockingQueue):
- 线程安全
- 提供阻塞的put/take方法
- 支持容量限制
- 实现类:ArrayBlockingQueue, LinkedBlockingQueue
非阻塞队列:
- 通常非线程安全(除并发实现)
- 操作立即返回
- 实现类:LinkedList, ArrayDeque, PriorityQueue
java复制// 阻塞队列示例
BlockingQueue<Integer> bq = new ArrayBlockingQueue<>(10);
new Thread(() -> {
try {
Integer item = bq.take(); // 阻塞直到有元素
process(item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
7.3 优先级队列的实现原理
PriorityQueue基于堆数据结构实现:
- 底层是平衡二叉堆
- 默认自然排序,可通过Comparator自定义
- 插入/删除时间复杂度O(log n)
- 非FIFO,按优先级出队
java复制Queue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
pq.poll(); // 返回1
pq.poll(); // 返回2
pq.poll(); // 返回3
7.4 线程安全队列的实现方式
Java提供多种线程安全队列实现:
-
阻塞式:
- ArrayBlockingQueue:基于数组+ReentrantLock
- LinkedBlockingQueue:基于链表+两把锁
- PriorityBlockingQueue:带优先级的阻塞队列
-
非阻塞式:
- ConcurrentLinkedQueue:基于CAS无锁算法
- ConcurrentLinkedDeque:双端队列版本
-
特殊用途:
- DelayQueue:元素延迟出队
- SynchronousQueue:直接传递队列
java复制// 并发性能对比
Queue<Integer> queue1 = new ConcurrentLinkedQueue<>(); // 高并发读
BlockingQueue<Integer> queue2 = new LinkedBlockingQueue<>(); // 生产消费模型
8. Java队列演进与新特性
8.1 Java 8的Stream支持
Queue作为Collection,完美支持Stream API:
java复制Queue<Order> orders = new LinkedList<>();
// 过滤+转换
List<String> ids = orders.stream()
.filter(o -> o.getAmount() > 100)
.map(Order::getId)
.collect(Collectors.toList());
8.2 Java 9的工厂方法
Java 9为集合添加了方便的工厂方法:
java复制Queue<String> queue = Queue.of("A", "B", "C"); // 不可变队列
Queue<String> empty = Queue.of(); // 空队列
8.3 Java 21的虚拟线程适配
虚拟线程(Virtual Thread)时代,阻塞队列的新用法:
java复制// 每个任务在虚拟线程中执行
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// 生产者
new Thread(() -> {
while(true) {
Runnable task = createTask();
queue.put(task); // 阻塞但不消耗OS线程
}
}).start();
// 消费者
new Thread(() -> {
while(true) {
Runnable task = queue.take();
executor.submit(task); // 轻量级虚拟线程
}
}).start();
9. 跨语言队列对比
9.1 Python的queue模块
Python通过queue模块提供队列实现:
- Queue:线程安全队列
- LifoQueue:后进先出队列
- PriorityQueue:优先级队列
python复制from queue import Queue
q = Queue()
q.put(1)
item = q.get() # 阻塞操作
与Java的主要区别:
- 内置线程安全
- 只有阻塞API
- 动态类型
9.2 C++的STL队列
C++标准库提供:
- queue:适配器,默认基于deque
- priority_queue:优先级队列
- 非线程安全
cpp复制#include <queue>
std::queue<int> q;
q.push(1);
int val = q.front();
q.pop();
特点:
- 模板化设计
- 性能导向
- 丰富的底层容器选择
9.3 Go的channel
Go语言的channel是更高级的队列抽象:
- 内置语言支持
- 线程安全通信原语
- 支持选择(select)操作
go复制ch := make(chan int, 10) // 缓冲通道
go func() {
ch <- 42 // 发送
}()
value := <-ch // 接收
对比Java:
- 更轻量级
- 深度集成并发模型
- 缺乏丰富的队列操作API
10. 实际工程经验分享
10.1 电商订单处理系统实践
在日均百万订单的系统中,我们采用多级队列架构:
- 接入层:ConcurrentLinkedQueue快速接收订单
- 缓冲层:ArrayBlockingQueue控制处理速率
- 优先级层:PriorityQueue处理VIP订单
- 重试层:DelayQueue实现自动重试
关键优化点:
- 每层队列独立监控
- 根据负载动态调整队列容量
- 优雅降级机制
java复制// 简化的多级队列实现
class OrderProcessingSystem {
private final Queue<Order> inboundQueue = new ConcurrentLinkedQueue<>();
private final BlockingQueue<Order> bufferQueue = new ArrayBlockingQueue<>(1000);
private final PriorityQueue<Order> vipQueue = new PriorityQueue<>(
Comparator.comparing(Order::isVip).reversed()
);
public void start() {
// 转移线程
new Thread(() -> {
while(!Thread.currentThread().isInterrupted()) {
Order order = inboundQueue.poll();
if(order != null) {
bufferQueue.offer(order);
}
}
}).start();
// 处理线程
new Thread(() -> {
while(!Thread.currentThread().isInterrupted()) {
Order order = bufferQueue.take();
if(order.isVip()) {
vipQueue.offer(order);
}
processOrder(order);
}
}).start();
}
}
10.2 日志收集系统队列设计
高吞吐日志系统的队列设计要点:
- 内存队列:ArrayDeque作为前端缓冲
- 磁盘溢出:队列满时写入临时文件
- 批量提交:积累一定数量后批量写入存储
- 优先级处理:错误日志优先处理
java复制class LogCollector {
private static final int BATCH_SIZE = 100;
private final Queue<LogEvent> memoryQueue = new ArrayDeque<>(1000);
public void collect(LogEvent event) {
if(!memoryQueue.offer(event)) {
writeToDisk(event); // 溢出处理
return;
}
if(memoryQueue.size() >= BATCH_SIZE) {
List<LogEvent> batch = new ArrayList<>(BATCH_SIZE);
for(int i=0; i<BATCH_SIZE; i++) {
batch.add(memoryQueue.poll());
}
asyncSaveToStorage(batch);
}
}
// 错误日志优先处理
public void collectError(LogEvent error) {
// 实现类似collect但优先处理
}
}
10.3 微服务通信中的队列应用
在微服务架构中,队列常用于:
- 服务解耦:通过消息队列连接服务
- 流量削峰:缓冲突发请求
- 失败重试:持久化队列存储失败请求
- 事件溯源:队列作为事件存储
典型模式:
java复制// 使用Spring Integration的队列通道
@Bean
public QueueChannel orderChannel() {
return new QueueChannel(1000); // 容量1000
}
@ServiceActivator(inputChannel = "orderChannel")
public void processOrder(Order order) {
// 处理订单
if(failed) {
retryQueue.offer(order); // 进入重试队列
}
}
// 定时处理重试队列
@Scheduled(fixedRate = 5000)
public void processRetries() {
while(!retryQueue.isEmpty()) {
Order order = retryQueue.poll();
try {
processOrder(order);
} catch(Exception e) {
deadLetterQueue.offer(order); // 进入死信队列
}
}
}
11. 性能测试与基准对比
11.1 主流队列实现性能测试
使用JMH进行基准测试,比较不同场景下的操作性能(ns/op):
| 操作 \ 实现 | LinkedList | ArrayDeque | ConcurrentLinkedQueue | ArrayBlockingQueue |
|---|---|---|---|---|
| offer() | 45 | 32 | 58 | 72 |
| poll() | 42 | 30 | 55 | 68 |
| peek() | 15 | 12 | 18 | 25 |
| 并发offer() | - | - | 120 | 180 |
| 并发poll() | - | - | 110 | 170 |
测试环境:JDK 17, 8-core CPU, 16GB RAM
关键发现:
- ArrayDeque在单线程下性能最优
- 并发场景下无锁实现(ConcurrentLinkedQueue)优势明显
- 阻塞队列因锁开销性能较低
11.2 容量规划建议
基于测试结果的容量规划指南:
-
内存考量:
- LinkedList:每个元素额外消耗24字节引用开销
- ArrayDeque:预分配连续内存,浪费少
- 经验公式:预估最大元素数 × 元素大小 × 1.5
-
并发级别:
- 低竞争(<100线程):ConcurrentLinkedQueue
- 高竞争:根据吞吐量选择分区队列
-
GC影响:
- LinkedList产生更多垃圾对象
- ArrayDeque对GC更友好
java复制// 容量计算示例
class QueueCapacityCalculator {
public static int calculate(int expectedMax, int elementSize) {
// 考虑对象头、对齐等开销
int objectOverhead = 16; // 对象头
int referenceSize = 4; // 压缩指针
// ArrayDeque计算
int arrayDequeSize = objectOverhead +
(expectedMax * (elementSize + referenceSize));
// LinkedList计算
int linkedListSize = objectOverhead +
(expectedMax * (objectOverhead*2 + elementSize + referenceSize*2));
return Math.min(arrayDequeSize, linkedListSize);
}
}
12. 扩展阅读与资源推荐
12.1 经典书籍章节
-
《Java并发编程实战》 - Brian Goetz
- 第5章:构建高效可伸缩的结果缓存
- 第7章:任务执行与Executor框架
-
《算法(第4版)》 - Robert Sedgewick
- 1.3节:背包、队列和栈
- 2.4节:优先队列
-
《数据结构与算法分析》 - Mark Allen Weiss
- 第6章:优先队列(堆)
12.2 开源项目参考
-
Netty的ChannelOutboundBuffer
- 高性能网络框架中的队列实现
- 结合了链表和数组的优点
-
Kafka的请求队列
- 分区有序队列实现
- 高效的磁盘持久化策略
-
RxJava的背压队列
- 响应式编程中的流量控制
- 多种队列策略选择
12.3 学习路线建议
-
基础阶段:
- 掌握Queue接口及其实现类
- 理解阻塞与非阻塞区别
- 熟练使用优先级队列
-
进阶阶段:
- 研究并发队列的实现原理
- 学习无锁算法(CAS)
- 分析JUC包中的队列源码
-
专家阶段:
- 设计自定义队列满足特殊需求
- 优化队列内存布局
- 实现分布式队列系统
java复制// 自定义环形缓冲区示例
class RingBuffer<E> {
private final E[] buffer;
private int head, tail;
@SuppressWarnings("unchecked")
public RingBuffer(int capacity) {
buffer = (E[]) new Object[capacity];
}
public synchronized boolean offer(E item) {
if((tail + 1) % buffer.length == head) return false;
buffer[tail] = item;
tail = (tail + 1) % buffer.length;
return true;
}
public synchronized E poll() {
if(head == tail) return null;
E item = buffer[head];
buffer[head] = null; // 帮助GC
head = (head + 1) % buffer.length;
return item;
}
}