JDK 竟然是这样实现栈的?

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

前面的文章《动图演示:手撸堆栈的两种实现方法!》我们用数组和链表来实现了自定义的栈结构,那在 JDK 中官方是如何实现栈的呢?接下来我们一起来看。

这正式开始之前,先给大家再解释一下「堆栈」一词的含义,因为之前有读者对这个词有一定的疑惑。

Stack 翻译为中文是堆栈的意思,但为了能和 Heap(堆)区分开,因此我们一般将 Stack 简称为栈。因此当“堆栈”连在一起时有可能表示的是 Stack,而当“堆、栈”中间有分号时,则表示 Heap(堆)和 Stack(栈),如下图所示:


JDK 栈的实现

聊会正题,接下来我们来看 JDK 中是如何实现栈的?

在 JDK 中,栈的实现类是 Stack,它的继承关系如下图所示:


Stack 包含的方法如下图所示:


其中最重要的方法有:

  • push:入栈方法(添加数据);

  • pop:出栈并返回当前元素(移除数据);

  • peek:查询栈顶元素。

Stack 实现源码如下:

public class Stack<E> extends Vector<E> {/*** 创建一个空栈*/public Stack() {}/*** 入栈方法,调用的是 Vector#addElement 的添加方法*/public E push(E item) {addElement(item);return item;}/*** 出栈并返回当前元素,调用的是 Vector#removeElementAt 的移除元素方法*/public synchronized E pop() {E       obj; // 返回当前要移除的栈顶元素信息int     len = size();obj = peek(); // 查询当前栈顶元素removeElementAt(len - 1); // 移除栈顶元素return obj;}/*** 查询栈顶元素,调用 Vector#elementAt 的查询方法*/public synchronized E peek() {int     len = size(); // 查询当前栈的长度if (len == 0) // 如果为空栈,直接抛出异常throw new EmptyStackException();return elementAt(len - 1); // 查询栈顶元素的信息}/*** 判断栈是否为空*/public boolean empty() {return size() == 0;}// 忽略其他方法...
}

从上述源码可以看出, Stack 中的核心方法中都调用了父类 Vector 类中的方法,Vector 类的核心源码:

public class Vector<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{protected Object[] elementData; // 存储数据的容器protected int elementCount; // 存储数据的容量值/*** 添加数据*/public synchronized void addElement(E obj) {modCount++; // 统计容器被更改的参数ensureCapacityHelper(elementCount + 1); // 确认容器大小,如果容量超出则进行扩容elementData[elementCount++] = obj; // 将数据存储到数组}/*** 移除元素(根据下标移除)*/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--; // 数组容量 -1elementData[elementCount] = null; // 将末尾的元素赋值为 null(删除尾部元素)}/*** 查询元素(根据下标)*/public synchronized E elementAt(int index) {// 安全性验证if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);}// 根据下标返回数组中的元素return elementData(index);}// 忽略其他方法...
}

对于上述源码中,可以最不好理解的就是 System#arraycopy 这个方法,它的作用其实就是将删除的元素(非末尾元素)的后续元素依次往前移动的,比如以下代码:

Object[] elementData = {"Java", "Hello", "world", "JDK", "JRE"};
int index = 3;
int j = elementData.length - index - 1;
System.arraycopy(elementData, index + 1, elementData, index, j);
//  System.arraycopy(elementData, 4, elementData, 3, 1);
System.out.println(Arrays.toString(elementData));

它的运行结果是:

[Java, Hello, world, JRE, JRE]

也就是说当我们要删除下标为 3 的元素时,需要把 3 以后的元素往前移动,所以数组的值就从 {"Java", "Hello", "world", "JDK", "JRE"} 变为了 [Java, Hello, world, JRE, JRE],最后我们只需要把尾部元素删除掉,就可以实现数组中删除非末尾元素的功能了。

小结

通过以上源码可以得知,JDK 中的栈(Stack)也是通过物理结构数组实现的,我们通过操作物理数组来实现逻辑结构栈的功能,关于物理结构和逻辑结构详见《动图演示:手撸堆栈的两种实现方法!》。

栈的应用

经过前面的学习我们对栈已经有了一定的了解了,那栈在我们的平常工作中有哪些应用呢?接下里我们一起来看。

浏览器回退

栈的特性为 LIFO(Last In First Out,LIFO)后进先出,因此借助此特性就可以实现浏览器的回退功能,如下图所示:

函数调用栈

栈在程序中最经典的一个应用就是函数调用栈了(或叫方法调用栈),比如操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构, 用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。为了让你更好地理解,我们一块来看下这段代码的执行过程。

int main() {int a = 1; int ret = 0;int res = 0;ret = add(3, 5);res = a + ret;System.out.println(res);reuturn 0;
}
int add(int x, int y) {int sum = 0;sum = x + y;return sum;
}

从代码中我们可以看出, main() 函数调用了 add() 函数,获取计算结果,并且与临时变量 a 相加,最后打印 res 的值。为了让你清晰地看到这个过程对应的函数栈里出栈、入栈的操作,我画了一张图。图中显示的是,在执行到 add() 函数时,函数调用栈的情况。

栈的复杂度

复杂度分为两个维度:

  • 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述;

  • 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。

这两种复杂度都是用大 O 表示法来表示的,比如以下代码:

int[] arr = {1, 2, 3, 4};
for (int i = 0; i < arr.length; i++) {System.out.println(i);
}

用大 O 表示法来表示的话,它的时间复杂度就是 O(n),而如下代码的时间复杂度却为 O(1):

int[] arr = {1, 2, 3, 4};
System.out.println(arr[0]); // 通过下标获取元素

因此如果使用大 O 表示法来表示栈的复杂度的话,结果如下所示:

引用 & 鸣谢

https://time.geekbang.org/column/article/41222


往期推荐

动图演示:手撸堆栈的两种实现方法!


Java新特性:数据类型可以扔掉了?


JDK15正式发布,新增功能预览!


关注下方二维码,收获更多干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

layui如何自定义layedit富文本编辑器工具栏

layui自带了layedit富文本编辑器&#xff0c;我们在使用过程中有时候不需要全部工具&#xff0c;或者想自定义工具栏&#xff0c;该如何弄呢&#xff1f;以下方法教大家自定义自己的layedit编辑器。 步骤1&#xff1a;定义存放编辑器的盒子 <div class"layui-form-it…

C++总结篇(2)类和对象

1、类 1.1类的定义: C是一门面向对象的语言&#xff0c;便就引入了类的概念&#xff0c;类在一定程度上与C语言中的结构体很相似。Class为定义类的关键字&#xff0c;如下示例&#xff1a; class student {char name[10];int age;int print(); };类不仅可以用class来定义&a…

小程序 || 语句_C ++条件语句| 查找输出程序| 套装1

小程序 || 语句Program 1: 程序1&#xff1a; #include <iostream>using namespace std;int main(){if (NULL) {cout << "Hello World";}else {cout << "Hii World";}return 0;}Output: 输出&#xff1a; Hii WorldExplanation: 说明&…

关于微信,运营商们就这点志向?

2019独角兽企业重金招聘Python工程师标准>>> 近期关于运营商威逼微信收费之事闹得沸沸扬扬&#xff0c;在虎嗅上看到有不少人发表了自己的看法也不乏给运营商或微信出点子的人&#xff0c;但我觉得都不是很妥&#xff0c;还是谈谈我的看法吧。 陈旧的思路&#xff…

阿里巴巴开源的Excel操作神器!

前提导出数据到Excel是非常常见的后端需求之一&#xff0c;今天来推荐一款阿里出品的Excel操作神器&#xff1a;EasyExcel。EasyExcel从其依赖树来看是对apache-poi的封装&#xff0c;笔者从开始接触Excel处理就选用了EasyExcel&#xff0c;避免了广泛流传的apache-poi导致的内…

再谈指针

C语言为什么高效&#xff1f;因为C语言有指针。指针是C语言的精华&#xff0c;同时也是C语言的难点&#xff0c;很多人一学到指针就表示头大&#xff0c;指针的指向往往把人搞得晕头转向&#xff0c;甚至有的人为了避免使用指针居然不惜多写几十行代码&#xff0c;无疑增加了工…

vc给exe更改图标

第一步&#xff1a;将制作好的ioc格式图标&#xff0c;拷贝到自己工程所在的res文件夹中第二步&#xff1a;在vc开发环境中&#xff0c;insert-->resourse--〉单击icon然后选择右边的import找到刚才添加到res中的图标文件第三步&#xff1a;将m_hIcon AfxGetApp()->Load…

人工智能ai知识_人工智能中基于知识的代理层

人工智能ai知识Every agent that has a knowledge base and an inference system is known as a knowledge-based agent. The knowledge base contains all the information the agent has. This information can either be the data that is embedded into the agent in prior…

Word 2003中为什么修改一个段落的文章结果整篇文档的格式都变?

问题比如说&#xff0c;我选定某一段把颜色改成***&#xff0c;结果整篇文档都变成***了&#xff0c;按撤退健&#xff0c;才能达到效果&#xff08;只有这段变成***&#xff0c;其他的不变&#xff09;。答案打开格式菜单中的[样式和格式]&#xff0c;找到样式中的“正文”。 …

链表反转的两种实现方法,后一种击败了100%的用户!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;链表反转是一道很基础但又非常热门的算法面试题&#xff0c;它也在《剑指Offer》的第 24 道题出现过&#xff0c;至于它有多…

C++总结篇(4)内存管理

C语言中用malloc/realloc/calloc/free进行空间的申请与释放&#xff0c;在C中用新的方式进行空间的申请与释放。 申请一个int型的空间并释放 //C语言&#xff1a;int *ptr(int)malloc(sizeof(int));free(ptr); //C: int *ptr new int;delete ptr; C的申请方式更为简洁方便&…

debug和release的区别

1。Debug和Release有什么区别&#xff0c;为什么要使用Release版本&#xff01; 2。怎么把Debug转成ReleaseDebug版本包括调试信息&#xff0c;所以要比Release版本大很多&#xff08;可能大数百K至 数M&#xff09;。至于是否需要DLL支持&#xff0c;主要看你采用的编译选项。…

人工智能ai 学习_人工智能中学习代理的要素

人工智能ai 学习As already discussed, the Learning agents have the capability to improve their knowledge base by Learning from their surroundings by themselves, without any help or input from the user or the client. 如已经讨论的那样&#xff0c; 学习代理可以…

squid代理服务器(捎带的SNAT)

1.传统代理传统代理可以隐藏IP地址 多用于Internet 在Linux中 默认没有安装squid 所以要安装 在red hat中 还要安装perl 语言包的支持 squid代理服务器需要两块网卡 首先保证你的流量是从linux服务器上过的 所以先保证做完SNAT可以互相通信1&#xff09;配置网络参数在试验中一…

MySQL开源工具推荐,有了它我卸了珍藏多年Nactive!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;最近无意间发现了一款开源免费的 MySQL 客户端管理工具&#xff0c;磊哥试用了两天感觉还行&#xff0c;所以今天推荐给各位…

C++总结篇(3)String类

string是表示字符串的字符串类&#xff0c;该类的接口与常规容器的接口基本相同&#xff0c;再添加了一些专门用来操作string的常规操作。string在底层实际是&#xff1a;basic_string模板类的别名&#xff0c;typedef basic_string<char, char_traits, allocator> strin…

memoryTraining记忆训练小游戏

无聊的时候用C写了一个记忆训练的小游戏、、、 灵感源于一个flash的小游戏学到C语言就用C语言实验了一下&#xff0c;做出来。好久以前的东西了&#xff0c;数组用的还不咋样&#xff0c;现在看看把数组下标0漏掉了、、、掉了修补了修补&#xff0c;先扔这儿吧。源码下载

动态调用动态库方法 .so

2019独角兽企业重金招聘Python工程师标准>>> 关于动态调用动态库方法说明 一、 动态库概述 1、 动态库的概念 日常编程中&#xff0c;常有一些函数不需要进行编译或者可以在多个文件中使用&#xff08;如数据库输入/输 出操作或屏幕控制等标准任务函数&#…

C++总结篇(5)vector

vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而且它的大小会被容器自…

清除缓存 c语言_如何用C语言设置,清除和切换单个位?

清除缓存 c语言Given a number and we have to 1) set a bit, 2) clear a bit and 3) toggle a bit. 给定一个数字&#xff0c;我们必须1)设置一个位&#xff0c;2)清除一个位&#xff0c;3)切换一个位。 1)设置一点 (1) Setting a bit) To set a particular bit of a number,…