Java栈和队列

Java栈和队列

    • 1.Stack
    • 2.Queue
    • 3.PriorityQueue
    • 小结
        • 欢迎关注公众号:一盐难进

队列用来存储一组有序的值,二者很相似,栈是一种先进后出的结构,也就是最后保存的元素最先被访问;队列是一种先进先出的结构,也就是最先保存的元素最先被访问。

1.Stack

栈通常是指先进后出的容器。有时候栈也被称为叠加栈,因为最后压入的元素,第一个弹出栈,有图有真相:
Java栈和队列_第1张图片

Stack 框架图:

Java栈和队列_第2张图片

可以看出 Stack 继承了 Vector,实现了 List 接口,也是 Collection 大家族中的一员。下面瞅瞅 Stack 的常规操作:

public static void main(String[] args) {
        //创建 Stack 对象
        Stack stack = new Stack<>();
        String str = "this is a stack";
        for (String s : str.split(" ")) {
            //将值放入栈中
            stack.push(s);
        }
        System.out.println(stack);
        System.out.println("栈的大小:" + stack.size());
        System.out.println("栈顶的值:" + stack.peek());
        System.out.println("删除并返回栈顶的值:" + stack.pop());
        System.out.println("删除栈顶后的栈:" + stack);
        //打印结果和上面相反
        while (!stack.empty()){
            System.out.print(stack.pop() + " ");
        }
 }

打印结果:

[this, is, a, stack]
栈的大小:4
栈顶的值:stack
删除并返回栈顶的值:stack
删除栈顶后的栈:[this, is, a]
a is this

从代码和打印结果可以看出,Stack 的操作和 LinkedLIst 的操作非常相似:

  • push(E item) 方法,将值压入栈的顶部;
  • size() 方法,得到栈的大小;
  • peek() 方法,返回栈顶元素,但是不删除该元素;
  • pop() 方法,删除并返回栈顶元素;
  • empty() 方法,判断栈是否为空,如果为空返回 true。

2.Queue

队列是一个典型的先进先出的容器,即从容器的一端放入事物然后从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列在并发编程中尤为重要,因为它可以安全地将对象从一个任务传输给另一个任务。

Queue 框架图:

Java栈和队列_第3张图片

通过框架图可以看到 LinkedList 实现了 Queue 接口并且提供了方法以支持队列的行为。简单示例:

public static void main(String[] args) {
        //通过 LinkedList 向上转型创建 Queue 对象
        Queue queue = new LinkedList<>();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            //向队列末尾添加元素
            queue.offer(random.nextInt(10));
            //queue.add(random.nextInt(10));
        }
        System.out.println("队列: " + queue);
        System.out.println("队列头:" + queue.peek());
        System.out.println("队列头:" + queue.element());
        System.out.println("删除并返回队列头:" + queue.poll());
        System.out.println("删除并返回队列头:" + queue.remove());
}

打印结果:

队列: [2, 6, 4, 5, 3, 5, 9, 6, 2, 6]
队列头:2
队列头:2
删除并返回队列头:2
删除并返回队列头:6

Queue 的使用似曾相识,这些方法在文章 List 简介 中已经介绍过,Queue 接口窄化了对 LinkedList 的方法的访问权限,只对外暴露了以下方法:

  • offer(E e) 方法和 add(E e) 方法一样,都是向队列的末尾添加一个元素;
  • peek() 方法 和 element() 方法都是在不删除的情况下返回队列的头元素,但是 peek() 方法在队列为空时返回 null,而 element() 方法则会抛出异常;
  • poll() 方法和 remove() 方法都是删除并返回队列头元素,但是 poll() 方法在队列为空的时候返回 null,而 remove() 方法则会抛出异常。

3.PriorityQueue

先进先出描述了最典型的队列规则,声明的是下一个弹出队列的元素应该是等待时间最长的元素。优先级队列声明下一个弹出元素是最需要的元素,也就是具有最高的优先级,当在 PriorityQueue 中添加一个对象时,这个对象会在队列中被排序,规则是对象在队列中的自然顺序,如果需要修改这个顺序,那么可以通过提供自己的 Comparator 来实现。

  /**
     * 打印 queue
     * @param queue
     */
    public static void printQueue(Queue queue){
        while(!queue.isEmpty()){
            System.out.print(queue.poll() + ",");
        }
    }

    public static void main(String[] args) {
        //创建一个整数集合
        List list = Arrays.asList(3,6,1,8,5,1,7,9,0,4);

        //创建 PriorityQueue 对象
        PriorityQueue priorityQueue = new PriorityQueue<>();
        priorityQueue.addAll(list);
        System.out.println("默认排序:");
        //打印
        printQueue(priorityQueue);
        System.out.println();

        //创建倒序的 PriorityQueue 对象
        priorityQueue = new PriorityQueue<>(list.size(), new Comparator() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        priorityQueue.addAll(list);
        System.out.println("倒序:");
        //打印
        printQueue(priorityQueue);
    }

打印结果:

默认排序:
0,1,1,3,4,5,6,7,8,9,
倒序:
9,8,7,6,5,4,3,1,1,0,

当使用 PriorityQueue 的无参构造器创建 PriorityQueue 优先级队列的时候,队列中元素的顺序默认是自然排序,如果需要制定排序规则,就需要调用 PriorityQueue 的有参构造器,然后自定义 Comparator 对象重写 compareTo() 方法。而且从打印结果来看,元素的重复是允许的。

中间还有一个小插曲,当使用 PriorityQueue 的 toString() 方法去打印的时候,发现顺序居然不对,最后使用了上述代码中的遍历的方式才打印出理想中的结果。PriorityQueue 的逻辑结构是一棵完全二叉树,存储结构其实就是一个数组,逻辑层次遍历的结果刚好是一个数组。

小结

本章介绍了 Java 的栈和队列的概念和基本使用,补充了优先级队列的相关内容,打印优先级队列的时候需要使用遍历的方式。

欢迎关注公众号:一盐难进

Java栈和队列_第4张图片

你可能感兴趣的:(Java)