Java Review (二十八、集合----- Queue 集合)

简介: Java Review (二十八、集合----- Queue 集合)

文章目录

队列(Queue)是一种经常使用的集合。Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:

  • 把元素添加到队列末尾;
  • 从队列头部取出元素。

超市的收银台就是一个队列:

Queue 用于模拟队列这种数据结构。

 Queue 接口中定义了如下几个方法 。

  • void add(Object e): 将指定元素加入此队列 的 尾部。
  • Object element() : 获取队列头部的元素 , 但是不删除该元素 。
  • boolean offer(Object e) : 将指定元素加入此队列的尾部。当使用有容量限制的队列时, 此方法通常比add(Object e)方法更好 。
  • Object peek(): 获取队列头部的元素 , 但是不删除该元素 。 如果此队列为空,则返回 null 。
  • Object poll() : 获取队列头部的元素 , 并删除该元素。如果此队列为空 , 则返回 null 。
  • Object remove() : 获取队列头部的元素 , 并删除该元素 。

API:java.util.Queue

PriorityQueue 实现类

PriorityQueue 是 一个比较标准的队列实现类。之所以说它是比较标准的队列实现 , 而不是绝对标准的队列实现 , 是因为  PriorityQueue 保存队列元的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序 。 因此当调用 peek()方法或者  poll()方法取出队列中的元素时,并不是取出最先进入队列的元素 , 而是取出队列中最小 的元素。从这个意义上来看 ,  PriorityQueue 己经违反了队列的最基本规则: 先进先出 ( FIFO ) 。

 下面程序示范了 PriorityQueue 队列的用法:

PriorityQueueTest.java

public class PriorityQueueTest
{
  public static void main(String[] args)
  {
    PriorityQueue pq = new PriorityQueue();
    // 下面代码依次向pq中加入四个元素
    pq.offer(6);
    pq.offer(-3);
    pq.offer(20);
    pq.offer(18);
    // 输出pq队列,并不是按元素的加入顺序排列
    System.out.println(pq); // 输出[-3, 6, 20, 18]
    // 访问队列第一个元素,其实就是队列中最小的元素:-3
    System.out.println(pq.poll());
  }
}

Prio rityQueue 不允许插入 null 元素,它还需要对队列元素进行排序 , PriorityQueue 的元素有两种排序方式 :

  • 自然排序 : 采用自然顺序的 PriorityQueue 集合中的元素必须实现Comparable 接口,而且应该是同 一个类的多个实例 , 否则可能导致 ClassCastException 异常 。
  • 定制排序 : 创建 PriorityQueue 队列 时, 传入一个 Comparator 对象 , 该对象负责对队列中的所有元素进行排序 。 采用定制排序时不要求队列元素实现Comparable 接口 。

PriortyQueue 队列对元素的要求与 TreeSet 对元素的要求基本一致。

API:java.util.PriorityQueue

Deque 接口与 ArrayDeque 实现类

Deque 接口是 Queue 接口的子接口 , 它代表一个双端队列, Deque 接口里定义了 一些双端队列的方法,这些方法允许从两端来操作队列的元素。

  • void addFirst(Object e): 将指定元素插入该双端队列的开头 。
  • void addLast(Object e): 将指定元素插入该双端队列的末尾 。
  • Iterator descendingIterator(): 返回该双端队列对应的迭代器,该法代器将以逆向顺序来法代队列
    中的元素 。
  • Object getFirst(): 获取但不删除双端队列的第 一个元素 。
  • Object getLast(): 获取但不删除双端队列的最后 一个元素 。
  • boolean offerFirst(Object e): 将指定元素插入该双端队列的开头 。
  • boolean offerLast(Object e): 将指定元素插入该双端队列的末尾 。
  • Object peekFirst(): 获取但不删除该双端队列的第 一个元素:如果此双端队列为 空 ,则返回 null 。
  • Object peekLast(): 获取但不删除该双端队列的最后 一个元素:如果此双端队列为空,则返回 null 。
  • Object pollFirst(): 获取并删除该双端队列的第 一个元素;如果此双端队列为空,则返回 null 。
  • Object pollLast() : 获取并删除该双端队列的最后 一个元素;如果此双端队列为空,则返回 null 。
  • Object pop() (栈方法) : pop 出该双端队列所表示的校的技顶元素 。 相当于 removeFirstO 。
  • void push(Object e) (栈方法) : 将 一 个元素 push 进该双端队列所表示的拢的技顶 。 相当于addFirst(e) 。
  • Object removeFirst(): 获取并删除该双端队列的第一个元素 。
  • Object removeFirstOccurrence(Object o): 删除该双端队列的第一次出现的元素 。
  • Object removeLast(): 获取并删除该双端队列的最后一个元素 。
  • boolean removeLastOccurrence(Object o): 删除该双端队列的最后一次出现的元素。

Deque 不仅可以 当成双端队列使用,而且可以被当成栈来使用,因为该类

里还包含了 pop ( 出栈)、 push (入栈)两个方法 。

Deque 的方法与 Queue 的方法对照表

image.png

Deque 的方法与 Stack 的方法对照表

image.png

API:java.util.Deque

Deque 接口提供了 一个典型的实现类: ArrayDeque ,从该名称就可以看出 , 它是一个基于数组实现的双端队列,创建  Deque 时同样可指定一个 numElements 参数 , 该参数用于指定 Object[]数组的长度 :如果不指定 numElements  参数, Deque 底层数组的长度为 16 。

ArrayList 和 ArrayDeque 两个集合类的实现机制基本相似,它们的底层都采用一个动态的、可重分配的 Object[]数纽来存储集合元素,当集合元素超出了该数组的容量时,系统会在底层重新分配一个 Object[]数组来存储集合元素 。

 下面程序示范了把 ArrayDeque 当成"栈"来使用 :

ArrayDequeStack.java

public class ArrayDequeStack
{
  public static void main(String[] args)
  {
    ArrayDeque stack = new ArrayDeque();
    // 依次将三个元素push入"栈"
    stack.push("疯狂Java讲义");
    stack.push("轻量级Java EE企业应用实战");
    stack.push("疯狂Android讲义");
    // 输出:[疯狂Android讲义, 轻量级Java EE企业应用实战, 疯狂Java讲义]
    System.out.println(stack);
    // 访问第一个元素,但并不将其pop出"栈",输出:疯狂Android讲义
    System.out.println(stack.peek());
    // 依然输出:[疯狂Android讲义, 疯狂Java讲义, 轻量级Java EE企业应用实战]
    System.out.println(stack);
    // pop出第一个元素,输出:疯狂Android讲义
    System.out.println(stack.pop());
    // 输出:[轻量级Java EE企业应用实战, 疯狂Java讲义]
    System.out.println(stack);
  }
}

 ArrayDeque也可以当成队列使用,此处 AπayDeque 将按"先进先出"的方式操作集合元素。例如如下程序 :

ArrayDequeQueue.java

public class ArrayDequeQueue
{
  public static void main(String[] args)
  {
    ArrayDeque queue = new ArrayDeque();
    // 依次将三个元素加入队列
    queue.offer("疯狂Java讲义");
    queue.offer("轻量级Java EE企业应用实战");
    queue.offer("疯狂Android讲义");
    // 输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
    System.out.println(queue);
    // 访问队列头部的元素,但并不将其poll出队列"栈",输出:疯狂Java讲义
    System.out.println(queue.peek());
    // 依然输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
    System.out.println(queue);
    // poll出第一个元素,输出:疯狂Java讲义
    System.out.println(queue.poll());
    // 输出:[轻量级Java EE企业应用实战, 疯狂Android讲义]
    System.out.println(queue);
  }
}

LinkedList 也实现了 Deque 接口,可以被当成双端队列来使用,因此既可以被当成"栈"来使用 ,也可以当成队列使用 。

API:java.util.ArrayDeque

各种线性表的性能分析

Java 提供的 List 就是一个线性表接口,而ArrayList、LinkedList 又是线性表的两种典型实现 : 基于数组的线性表和基于链的线性表。 Queue 代表了队列, Deque 代表了双端队列(既可作为队列使用, 也可作为栈使用) 。

一般来说,

  • 由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能都比较好;
  • 而内部以链表作为底层实现的集合在执行插入、删除操作时有较好的性能 。

但总体来说, ArrayList 的性能比 LinkedList 的性能要好 , 因此大部分时候都应该考虑使用ArrayList。

 关于使用 List 集合有如下建议:

  • 如果需要遍历 List 集合元素,对于 ArrayList 、 Vector 集合,应该使用随机访问方法 (get) 来遍历集合元素,这样性能更好;对于 LinkedList 集合 ,则应该采用法代器 (Iterator ) 来遍历集合元素。
  • 如果 需要经常执行插入、删除操作来改变包含大量数据的 List 集合的大小,可考虑使用LinkedList 集合 。 使用 ArrayList 、 Vector 集合可能需要经常重新分配内部数组的大小, 效果可能较差 。
  • 如果有多个线程需要同时访 问 List 集合中的元素,开发者可考虑使用 Collections 将集合包装成线程安全的集合 。



参考:

【1】:《疯狂Java讲义》

【2】:廖雪峰的官方网站:使用Queue

【3】:廖雪峰的官方网站:使用PriorityQueue

【4】:廖雪峰的官方网站:使用Deque


目录
相关文章
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
43 3
|
27天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
27 2
|
1月前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
27天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
1月前
|
Java 开发者
|
2月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
60 5
|
2月前
|
安全 Java 程序员
Java集合之战:ArrayList vs LinkedList,谁才是你的最佳选择?
本文介绍了 Java 中常用的两个集合类 ArrayList 和 LinkedList,分析了它们的底层实现、特点及适用场景。ArrayList 基于数组,适合频繁查询;LinkedList 基于链表,适合频繁增删。文章还讨论了如何实现线程安全,推荐使用 CopyOnWriteArrayList 来提升性能。希望帮助读者选择合适的数据结构,写出更高效的代码。
70 3
|
1月前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
28 0
|
1月前
|
Java API Apache
java集合的组内平均值怎么计算
通过本文的介绍,我们了解了在Java中计算集合的组内平均值的几种方法。每种方法都有其优缺点,具体选择哪种方法应根据实际需求和场景决定。无论是使用传统的循环方法,还是利用Java 8的Stream API,亦或是使用第三方库(如Apache Commons Collections和Guava),都可以有效地计算集合的组内平均值。希望本文对您理解和实现Java中的集合平均值计算有所帮助。
31 0