Java 集合深入理解(13):Stack 栈

今天心情不错,再来一篇 Stack !

数据结构中的 栈

数据结构中,栈是一种线性数据结构,遵从 LIFO(后进先出)的操作顺序,所有操作都是在顶部进行

有点像羽毛球筒:

栈通常有三种操作:

  • push 入栈
  • pop 栈顶元素出栈,并返回
  • peek 获取栈顶元素,并不删除

我们自定义一个 栈 时只要实现上述三个主要操作即可,本文中将使用 Java 中的 LinkedList 实现一个栈。

栈的使用场景:

栈最主要的意义就在于:入栈和出栈的对称性。

在 Android 开发中,我们经常需要开启、回退一个 Activity,其实这里就有栈的应用,每次开启Activity,如果不是特殊的启动模式,就会在栈顶加入一个 Activity,点击返回后,之前的 Activity 出栈 。

其他场景比如递归(斐波那契数列,汉诺塔)。

Java 集合框架中的栈 Stack

Java 集合框架中的 Stack 继承自 Vector

  • 由于 Vector 有 4 个构造函数,加上 Stack 本身的一种,也就是说有 5 中创建 Stack 的方法
  • 跟 Vector 一样,它是 数组实现的栈

Stack 的方法

Stack 中新建的方法比较少:

1.构造函数

//构建一个空栈
public Stack() {
}

2.入栈

//调用的 Vector.addElement()
public E push(E item) {
    addElement(item);

    return item;
}

Vector 的 addElement() 方法,就是在数组尾部添加元素:

public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}

3.获取顶端元素,但不删除

public synchronized E peek() {
    //调用 Vector.size() 返回元素个数
    int     len = size();

    if (len == 0)
        throw new EmptyStackException();
    //调用 Vector.elementAt 得到栈顶元素
    return elementAt(len - 1);
}

Vector.elementAt(int):

public synchronized E elementAt(int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
    }

    return elementData(index);
}

Vector.elementData(int):

E elementData(int index) {
    return (E) elementData[index];
}

4.出栈

public synchronized E pop() {
    E       obj;
    int     len = size();

    //调用 peek() 获取顶端元素,一会儿返回
    obj = peek();
    //调用 Vector.removeElementAt 删除顶端元素
    removeElementAt(len - 1);

    return obj;
}

Vector.removeElementAt(int):

public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }

    int j = elementCount - index - 1;
    if (j > 0) {
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    elementCount--;
    elementData[elementCount] = null; /* to let gc do its work */
}

5.查找元素是否在栈中

public synchronized int search(Object o) {
    int i = lastIndexOf(o);

    //返回的是栈顶到该元素出现的位置的距离
    if (i >= 0) {
        return size() - i;
    }
    return -1;
}

6.是否为空

public boolean empty() {
    return size() == 0;
}

Vector.size():

public synchronized int size() {
    return elementCount;
}

总结

Java 集合框架中的 Stack 具有以下特点:

  • 继承自 Vector
  • 有 5 种创建 Stack 的方法
  • 采用数组实现
  • 除了 push(),剩下的方法都是同步的

用链表实现一个栈?

由于 Stack 是用数组实现的,我们用链表实现一下吧,这里就选择 LinkedList 来实现:

/**
 * description:LinkedList 模拟 Stack
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 10/23/2016
 */
public class LinkedListStack extends LinkedList{
    public LinkedListStack(){
        super();
    }

    @Override
    public void push(Object o) {
        super.push(o);
    }

    @Override
    public Object pop() {
        return super.pop();
    }

    @Override
    public Object peek() {
        return super.peek();
    }

    @Override
    public boolean isEmpty() {
        return super.isEmpty();
    }

    public int search(Object o){
        return indexOf(o);
    }
}

调用:

@Test
public void testPush() throws Exception {
    LinkedListStack stack = new LinkedListStack();
    System.out.println("栈是否为空: " + stack.isEmpty());

    stack.push("shixin");
    stack.push("好帅");
    stack.push("技巧一流");
    stack.push("haha");

    System.out.println("栈中元素: " + stack);

    System.out.println("获取顶端元素 peek :" + stack.peek());

    System.out.println("顶端元素出栈 pop :" + stack.pop());

    System.out.println("出栈后栈内元素:" + stack);

    System.out.println("search(好帅) 的位置:" + stack.search("好帅"));
}
}

结果:

可以看到,我其实都没做什么哈哈,都是 LinkedList 内部提供的方法,操作的都是在链表头部的元素,而不是尾部。

其实 LinkedList 这个栈的特性也是继承自 双端队列 Deque,官方也推荐在使用栈时优先使用 Deque,而不是 Stack,有兴趣的可以去了解下。

Thanks

https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html

http://www.cnblogs.com/kaituorensheng/archive/2013/03/02/2939690.html

http://www.nowamagic.net/librarys/veda/detail/2298

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android机动车

Java 基础(六)——集合源码解析 Queue

Queue继承自 Collection,我们先来看看类结构吧,代码量比较少,我直接贴代码了。

541
来自专栏非著名程序员

Java集合总览

这篇文章总结了所有的Java集合(Collection)。主要介绍各个集合的特性和用途,以及在不同的集合类型之间转换的方式。 Arrays Array是Java...

2017
来自专栏程序员的知识天地

Python实现堆栈

堆栈是一个后进先出的数据结构,其工作方式就像一堆汽车排队进去一个死胡同里面,最先进去的一定是最后出来。

702
来自专栏Hongten

python开发_python中的list操作

============================================

1094
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-day03-基础题

Java基础-day03-基础题 1.Scanner类,练习案例 Test1,定义main()方法,按以下步骤编写代码: A.导入Scanner类; B.在ma...

3025
来自专栏守候书阁

js数组操作--使用迭代方法替代for循环

数组的迭代方法,这个想必大家都不陌生了,可能刚入门的人暂时还没接触到这个。但是以后的开发中,肯定会用得上的。我自身的一个使用经历就是,如果迭代方法用的适当,不但...

643
来自专栏个人分享

JAVA源码走读(一) HashMap与ArrayList

HashMap是基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变...

722
来自专栏我是业余自学C/C++的

循环链表(测试代码,Qt5.1 for windows)

2014
来自专栏C/C++基础

不用加号实现两整数相加

对于二进制的加法运算,若不考虑进位,则1+1=0,1+0=1,0+1=1,0+0=0,通过对比异或,不难发现,此方法与异或运算类似。因而排出进位,加法可用异或来...

392
来自专栏逸鹏说道

我为NET狂面试题-基础篇-答案

面向过程: 答案:图片只贴核心代码,完整代码请打开解决项目查看 (答案不唯一,官方答案只供参考,若有错误欢迎提出~) 99乘法表 https://githu...

32713

扫码关注云+社区