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 条评论
登录 后参与评论

相关文章

来自专栏深度学习那些事儿

在pytorch中实现与TensorFlow类似的"same"方式padding

文章来自Oldpan博客:https://oldpan.me/archives/pytorch-same-padding-tflike

9327
来自专栏wOw的Android小站

[Tensorflow] 在Android运行TensorFlow模型

以下代码来自于TensorFlowObjectDetectionAPIModel.java

671
来自专栏null的专栏

数据结构和算法——用动态规划求解最短路径问题

一、动态规划求解问题的思路     在《算法导论》上,动态规划的求解过程主要分为如下的四步: 描述最优解的结构 递归定义最优解的值 按自底向上的方式计算最优解的...

3355
来自专栏简书专栏

Python数据科学库-小测验

答:np.arange、np.array、np.ones、np.zeros、np.full

711
来自专栏CDA数据分析师

Python之numpy数组学习(二)

前言 前面我们学习了numpy库的简单应用,今天来学习下比较重要的如何处理数组。 处理数组形状 下面可将多维数组转换成一维数组时的情形。 利用以下函数处理数组的...

1768
来自专栏漫漫深度学习路

pytorch学习笔记(七):pytorch hook 和 关于pytorch backward过程的理解

pytorch 的 hook 机制 在看pytorch官方文档的时候,发现在nn.Module部分和Variable部分均有hook的身影。感到很神奇,因为在使...

4735
来自专栏决胜机器学习

PHP数据结构(五) ——数组的压缩与转置

PHP数据结构(五)——数组的压缩与转置 (原创内容,转载请注明来源,谢谢) 1、数组可以看作是多个线性表组成的数据结构,二维数组可以有两种存储方式:一种是以行...

33911
来自专栏从零开始学自动化测试

pytest文档11-assert断言

断言是写自动化测试基本最重要的一步,一个用例没有断言,就失去了自动化测试的意义了。什么是断言呢? 简单来讲就是实际结果和期望结果去对比,符合预期那就测试pass...

724
来自专栏Java架构沉思录

从节省Redis内存空间说开去

上周部门会议上讨论的一个议题是如何节省Redis内存空间,其中有个小伙伴提到可以从压缩字符串入手,我觉得这是一个可以尝试的思路。因为有时候我们存在Redis中的...

982
来自专栏King_3的技术专栏

完全多部图的判断(个人思考)

给定一张包含N个点、M条边的无向图,每条边连接两个不同的点,且任意两点间最多只有一条边。对于这样的简单无向图,如果能将所有点划分成若干个集合,使得任意两个同一集...

923

扫码关注云+社区