0

0

java代码怎样实现队列及入队出队操作 java代码队列数据结构的实用实现方法​

星夢妙者

星夢妙者

发布时间:2025-08-11 18:17:02

|

313人浏览过

|

来源于php中文网

原创

最直接且推荐的方式是使用java.util.queue接口的实现类如linkedlist或arraydeque,1. 入队操作应优先使用offer()方法,因其在队列满时返回false而非抛出异常;2. 出队操作应优先使用poll()方法,因其在队列为空时返回null而非抛出异常;3. 查看头部元素应使用peek()方法以避免移除元素;4. 使用queue接口而非直接操作list能更好表达fifo意图并避免误用;5. linkedlist基于双向链表,适合频繁动态增删的场景,但内存开销大;6. arraydeque基于环形数组,性能更优、内存效率高,是多数场景下的首选;7. 在多线程环境下,应使用java.util.concurrent包中的线程安全队列,如concurrentlinkedqueue(非阻塞、高吞吐)、linkedblockingqueue(可阻塞、支持有界)或arrayblockingqueue(固定容量、基于数组);8. 应根据是否需要阻塞、容量限制和性能需求选择合适的并发队列,避免手动同步非线程安全的队列实现,以确保正确性和性能。

java代码怎样实现队列及入队出队操作 java代码队列数据结构的实用实现方法​

Java中实现队列及其入队出队操作,最直接且推荐的方式是利用

java.util.Queue
接口及其具体的实现类,比如
LinkedList
ArrayDeque
。这些类提供了符合队列先进先出(FIFO)原则的标准方法,让我们可以方便地管理数据的流入与流出。

解决方案

在Java中,队列(Queue)是一种重要的数据结构,它遵循“先进先出”(FIFO, First-In-First-Out)的原则。这意味着第一个进入队列的元素也将是第一个离开队列的。Java集合框架提供了

java.util.Queue
接口,以及多种实现类来满足不同的需求。

要实现队列及其入队(enqueue)和出队(dequeue)操作,我们通常会选择

LinkedList
ArrayDeque
。它们都实现了
Queue
接口,并提供了以下核心方法:

立即学习Java免费学习笔记(深入)”;

  • 入队操作(Enqueue)

    • offer(E e)
      :将指定元素插入队列的尾部。如果队列已满,此方法会返回
      false
      ,而不会抛出异常。这是推荐的入队方法。
    • add(E e)
      :与
      offer()
      类似,但如果队列已满(对于有容量限制的队列),它会抛出
      IllegalStateException
  • 出队操作(Dequeue)

    • poll()
      :获取并移除队列的头部元素。如果队列为空,此方法会返回
      null
      。这是推荐的出队方法。
    • remove()
      :与
      poll()
      类似,但如果队列为空,它会抛出
      NoSuchElementException
  • 查看头部元素(Peek)

    • peek()
      :获取但不移除队列的头部元素。如果队列为空,此方法会返回
      null
    • element()
      :与
      peek()
      类似,但如果队列为空,它会抛出
      NoSuchElementException

以下是一个使用

LinkedList
作为队列实现的简单示例:

import java.util.LinkedList;
import java.util.Queue;

public class SimpleQueueExample {
    public static void main(String[] args) {
        // 声明一个Queue接口类型的变量,使用LinkedList实现
        Queue messageQueue = new LinkedList<>();

        System.out.println("队列是否为空? " + messageQueue.isEmpty()); // true

        // 入队操作:使用 offer()
        messageQueue.offer("消息A");
        messageQueue.offer("消息B");
        messageQueue.offer("消息C");
        System.out.println("入队后队列: " + messageQueue); // [消息A, 消息B, 消息C]

        // 查看头部元素:使用 peek()
        String headMessage = messageQueue.peek();
        System.out.println("队列头部元素 (不移除): " + headMessage); // 消息A
        System.out.println("查看后队列: " + messageQueue); // [消息A, 消息B, 消息C]

        // 出队操作:使用 poll()
        String dequeuedMessage1 = messageQueue.poll();
        System.out.println("出队元素1: " + dequeuedMessage1); // 消息A
        System.out.println("出队后队列: " + messageQueue); // [消息B, 消息C]

        String dequeuedMessage2 = messageQueue.poll();
        System.out.println("出队元素2: " + dequeuedMessage2); // 消息B
        System.out.println("出队后队列: " + messageQueue); // [消息C]

        // 尝试从空队列出队
        messageQueue.poll(); // 移除消息C
        String emptyPollResult = messageQueue.poll();
        System.out.println("从空队列出队结果: " + emptyPollResult); // null
        System.out.println("最终队列: " + messageQueue); // []

        System.out.println("队列是否为空? " + messageQueue.isEmpty()); // true
    }
}

选择

offer()
/
poll()
/
peek()
而非
add()
/
remove()
/
element()
是更健壮的做法,尤其是在处理可能达到容量限制或可能为空的队列时,它们通过返回值而非抛出异常来指示操作结果,这在很多场景下更易于错误处理。

为什么在Java中我们更倾向于使用
Queue
接口而不是直接操作
List
实现队列?

这其实是个很好的问题,尤其对于初学者来说,可能会觉得

LinkedList
本身就是个
List
,为什么不直接用它的
add()
remove(0)
来模拟队列呢?在我看来,这主要关乎“意图表达”和“契约保证”。

当你声明一个变量为

Queue myQueue = new LinkedList<>();
时,你向所有阅读这段代码的人明确地表达了:这个集合的目的是作为一个队列来使用,它将遵循FIFO原则。这种明确性非常重要。如果我看到一个
List myList = new LinkedList<>();
,我不会立刻知道它是不是在扮演队列的角色,它可能被用于任何
List
的操作,比如随机访问
get(index)
,或者在中间插入元素
add(index, element)
,而这些操作在队列的语境下通常是不被允许或不推荐的。

Queue
接口的方法(
offer
,
poll
,
peek
)是专门为队列行为设计的,它们有清晰的语义和预期的行为,例如
poll()
在队列为空时返回
null
而不是抛出异常,这使得错误处理更加优雅。直接操作
List
的方法,比如
remove(0)
,在列表为空时会抛出
IndexOutOfBoundsException
,这在处理队列时可能需要额外的
try-catch
块,显得不够自然。

AdMaker AI
AdMaker AI

从0到爆款高转化AI广告生成器

下载

所以,使用

Queue
接口不仅提升了代码的可读性和可维护性,它还通过接口的约束,强制开发者以队列的方式来使用这个数据结构,避免了潜在的误用。这就像你买了一个专门用来烧水的电水壶,而不是用一个普通的锅在炉子上烧水——两者都能烧水,但电水壶的设计更符合烧水的特定场景,用起来也更安全、更方便。

LinkedList
ArrayDeque
作为队列实现,它们各自的适用场景与性能考量是什么?

在Java中,

LinkedList
ArrayDeque
是实现
Queue
接口最常用的两个类,但它们底层的数据结构和性能特性却大相径庭,因此在选择时需要根据具体场景来权衡。

LinkedList

  • 底层实现: 双向链表。每个元素都包含指向前后元素的引用。
  • 优点:
    • 动态性强: 插入和删除操作(无论在头部、尾部还是中间)的平均时间复杂度都是O(1),因为只需要修改少数几个节点的引用。这意味着它在频繁进行入队出队操作时表现稳定。
    • 内存使用灵活: 不需要预先分配固定大小的内存空间,可以根据需要动态增长。
    • 可作双端队列(Deque):
      LinkedList
      同时实现了
      Deque
      接口,这意味着它也可以作为栈(Stack)来使用,支持在两端进行高效的添加和移除。
  • 缺点:
    • 内存开销大: 每个元素除了存储数据本身,还需要额外的内存来存储前后节点的引用,导致内存效率相对较低。
    • 缓存不友好: 链表中的元素在内存中不一定是连续存储的,这可能导致CPU缓存命中率低,从而影响性能。
    • 随机访问慢: 查找特定位置的元素需要从头或尾遍历,时间复杂度为O(n)。
  • 适用场景:
    • 当队列的容量需要频繁地、大幅度地变化,且对内存的连续性要求不高时。
    • 当你需要一个既能作为队列也能作为栈,或者需要支持双端操作的数据结构时。
    • 对随机访问性能不敏感的场景。

ArrayDeque

  • 底层实现: 动态可调整大小的数组。它是一个环形数组(circular array),通过两个指针(头指针和尾指针)来管理元素的添加和移除。
  • 优点:
    • 性能优异: 对于入队和出队操作,其时间复杂度通常为O(1)(摊还常数时间),因为它利用了数组的连续性,缓存命中率高。在大多数情况下,它比
      LinkedList
      更快。
    • 内存效率高: 不需要额外的节点引用开销,存储效率更高。
    • 可作双端队列(Deque):
      ArrayDeque
      也实现了
      Deque
      接口,同样可以高效地作为栈或双端队列使用。
  • 缺点:
    • 扩容开销: 当内部数组空间不足时,需要进行扩容操作(创建一个更大的新数组并将旧数组的元素复制过去),这可能导致单次操作的开销较大(尽管摊还下来是O(1))。
  • 适用场景:
    • 绝大多数的队列和栈场景: 尤其是在对性能有较高要求,且队列大小变化相对平稳(不会频繁触发扩容)时。
    • 作为普通队列或栈使用时,
      ArrayDeque
      通常是首选,因为它在性能上往往优于
      LinkedList

我的选择倾向: 说实话,对于大多数“纯粹”的FIFO队列应用,我个人会优先考虑

ArrayDeque
。它的性能优势在实际项目中往往非常明显,尤其是在处理大量数据时。只有当我知道我可能需要链表的某些特定优势(比如在队列中间进行插入删除,或者对内存碎片有特别的考量,尽管这在队列场景下不常见)时,我才会考虑
LinkedList
。当然,如果只是一个很小的队列,性能差异可能微乎其微,那么选择哪个都无伤大雅。

在多线程环境下,Java队列的实现有哪些特殊考虑和推荐实践?

在多线程环境下使用队列,情况会变得复杂得多。标准的

LinkedList
ArrayDeque
都不是线程安全的,这意味着如果多个线程同时对它们进行入队或出队操作,很可能会导致数据损坏、丢失,或者出现意想不到的错误(比如
ConcurrentModificationException
)。我曾经就掉过这样的坑,调试起来简直是噩梦。

因此,在多线程编程中,我们必须使用专门设计用于并发访问的队列实现。Java的

java.util.concurrent
包为我们提供了强大的工具

  1. ConcurrentLinkedQueue
    :非阻塞队列

    • 特点: 这是一个基于链表的、线程安全的队列。它的特点是“非阻塞”,意味着当一个线程尝试入队或出队时,如果操作无法立即完成(例如队列为空),它不会阻塞该线程,而是通过CAS(Compare-And-Swap)操作等无锁算法来保证线程安全。
    • 优点: 高并发性能,特别适合生产者-消费者模型中,生产者和消费者数量都很多,且不希望线程因为队列满或空而长时间等待的场景。它内部的实现非常精巧,避免了锁的开销。
    • 缺点: 不支持有界队列(即容量无限)。
    • 适用场景: 高吞吐量的日志系统、事件分发系统等。
  2. LinkedBlockingQueue
    :有界/无界阻塞队列

    • 特点: 这是一个基于链表的、线程安全的阻塞队列。它支持可选的容量限制。当队列满时,尝试入队的线程会被阻塞,直到队列有空间;当队列空时,尝试出队的线程会被阻塞,直到队列有元素。
    • 优点: 提供了生产者-消费者模式中常见的流量控制机制。你可以通过构造函数指定其容量,从而防止内存溢出。
    • 缺点: 线程会因为队列状态而阻塞,在高并发下可能引入额外的上下文切换开销。
    • 适用场景: 大多数经典的生产者-消费者模型,例如消息队列、任务调度器等,需要对生产者或消费者进行流量控制的场景。
  3. ArrayBlockingQueue
    :有界阻塞队列

    • 特点: 这是一个基于数组的、线程安全的阻塞队列。它必须在创建时指定容量,且容量不可变。
    • 优点: 内部使用数组实现,具有更好的局部性,在某些情况下可能比
      LinkedBlockingQueue
      有更好的性能表现(但通常差异不大)。同样提供了阻塞机制和容量控制。
    • 缺点: 容量固定,一旦创建无法改变。
    • 适用场景: 当你需要一个固定容量的队列,且对性能有较高要求时。例如线程池中的任务队列。

推荐实践:

  • 明确需求: 首先要搞清楚你的队列是需要阻塞行为(即生产者在队列满时等待,消费者在队列空时等待)还是非阻塞行为。
  • 容量考量: 如果你需要限制队列的最大容量以防止资源耗尽,那么
    LinkedBlockingQueue
    ArrayBlockingQueue
    是你的选择。
  • 性能权衡: 对于大多数情况,
    LinkedBlockingQueue
    是一个非常通用且性能不错的选择。如果你追求极致的非阻塞性能,且不介意队列无界,可以考虑
    ConcurrentLinkedQueue
  • 避免手动同步: 永远不要尝试自己用
    synchronized
    ReentrantLock
    去包装
    LinkedList
    ArrayDeque
    来实现线程安全队列。这不仅容易出错,而且性能往往不如
    java.util.concurrent
    包中经过精心设计和优化的实现。
  • 选择最合适的: 没有“万能”的线程安全队列。理解它们各自的特点,根据你的具体应用场景(如吞吐量要求、是否需要容量限制、是否允许阻塞等)来选择最合适的实现。

在我的经验中,当涉及到多线程协作时,直接使用

java.util.concurrent
包提供的队列是唯一可靠且高效的做法。它们不仅保证了数据一致性,还考虑了各种并发场景下的性能优化,省去了我们大量重复造轮子和调试并发问题的精力。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

834

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

9

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 2.7万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.6万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号