java集合【13】——— Stack源码分析走一波
前言
集合源码分析系列:Java集合源码分析
前面已经把Vector
,ArrayList
,LinkedList
分析完了,本来是想开始Map
这一块,但是看了下面这个接口设计框架图:整个接口框架关系如下(来自百度百科):
原来还有一个漏网之鱼,Stack
栈的是挂在Vector
下,前面我们已经分析过Vector
了,那么顺便把Stack
分析一遍。再不写就2022年了:
Stack介绍
栈是一种数据结构,并不是Java
特有的,在Java
里面体现是Stack
类。它的本质是先进后出,就像是一个桶,只能不断的放在上面,取出来的时候,也只能不断的取出最上面的数据。要想取出底层的数据,只有等到上面的数据都取出来,才能做到。当然,如果有这种需求,我们一般会使用双向队列。
以下是栈的特性演示:
Stack
在Java
中是继承于Vector
,这里说的是1.8
版本,共用了Vector
底层的数据结构,底层都是使用数组实现的,具有以下的特点:
- 先进后出(``FILO`)
- 继承于
Vector
,同样基于数组实现 - 由于使用的几乎都是
Vector
,Vector
的操作都是线程安全的,那么Stack
操作也是线程安全的。
类定义源码:
public
class Stack<E> extends Vector<E> {
}
成员变量只有一个序列化的变量:
private static final long serialVersionUID = 1224463164541339165L;
方法解读
Stack
没有太多自己的方法,几乎都是继承于Vector
,我们可以看看它自己拓展的 5 个方法:
E push(E item)
: 压栈synchronized E pop()
: 出栈synchronized E peek()
:获取栈顶元素boolean empty()
:判断栈是不是为空int search(Object o)
: 搜索某个对象在栈中的索引位置
push 方法
在底层实际上调用的是addElement()
方法,这是Vector
的方法:
public E push(E item) {
addElement(item);
return item;
}
在前面我们已经分析过Vecor
的源码,感兴趣可以参考:http://aphysia.cn/archives/java-ji-he-11vector-chao-ji-xiang-xi-yuan-ma-jie-xi
addElement
是线程安全的,在底层实际上就是往数组后面添加了一个元素,但是它帮我们保障了容量,如果容量不足,会触发自动扩容机制。
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
pop 方法
底层是先调用peek()
方法,获取到栈顶元素,再调用removeElementAt()
方法移除掉栈顶元素,实现出栈效果。
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
removeElementAt(int index)
也是Vector
的方法,synchronized
修饰,也是线程安全的,由于移除的是数组最后的元素,所以在这里不会触发元素复制,也就是System.arraycopy(elementData, index + 1, elementData, index, j);
:
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 */
}
peek 方法
获取栈顶元素,先获取数组的大小,然后再调用Vector
的E elementAt(int index)
获取该索引的元素:
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
E elementAt(int index)
的源码如下,里面逻辑比较简单,只有数组越界的判断:
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
empty 方法
主要是用来判空,判断元素栈里面有没有元素,主要调用的是size()
方法:
public boolean empty() {
return size() == 0;
}
这个size()
方法其实也是Vector
的方法,返回的其实也是一个类变量,元素的个数:
public synchronized int size() {
return elementCount;
}
search方法
这个方法主要用来查询某个元素的索引,如果里面存在多个,那么将会返回最后一个元素的索引:
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
使用synchronized
修饰,也是线程安全的,为什么需要这个方法呢?
我们知道栈是先进先出的,如果需要查找一个元素在其中的位置,那么需要一个个取出来再判断,那就太麻烦了,而底层使用数组进行存储,可以直接利用这个特性,就可以快速查找到该元素的索引位置。
至此,回头一看,你是否会感到疑惑,``Stack里面没有任何的数据,但是却不断的在操作数据,这得益于
Vector`的数据结构:
// 底层数组
protected Object[] elementData;
// 元素数量
protected int elementCount;
底层使用数组,保存了元素的个数,那么操作元素其实只是不断往数组中插入元素,或者取出最后一个元素即可。
总结
stack
由于继承了Vector
,因此也是线程安全的,底层是使用数组保存数据,大多数API
都是使用Vector
来保存。它最重要的属性是先进先出。至于数组扩容,沿用了Vector
中的扩容逻辑。
如果让我们自己实现,底层不一定使用数组,使用链表也是能实现相同的功能的,只是在整个集合源码体系中,共有相同的部分,是不错的选择。
【作者简介】:
秦怀,公众号【秦怀杂货店】作者,个人网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使缓慢,驰而不息。
java集合【13】——— Stack源码分析走一波的更多相关文章
- Java集合之Stack 源码分析
1.简介 栈是数据结构中一种很重要的数据结构类型,因为栈的后进先出功能是实际的开发中有很多的应用场景.Java API中提供了栈(Stacck)的实现,简单使用如下所示 package com.tes ...
- 死磕 java集合之ConcurrentHashMap源码分析(三)
本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...
- 【死磕 Java 集合】— ConcurrentSkipListMap源码分析
转自:http://cmsblogs.com/?p=4773 [隐藏目录] 前情提要 简介 存储结构 源码分析 主要内部类 构造方法 添加元素 添加元素举例 删除元素 删除元素举例 查找元素 查找元素 ...
- 死磕 java集合之DelayQueue源码分析
问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...
- 死磕 java集合之PriorityBlockingQueue源码分析
问题 (1)PriorityBlockingQueue的实现方式? (2)PriorityBlockingQueue是否需要扩容? (3)PriorityBlockingQueue是怎么控制并发安全的 ...
- 死磕 java集合之PriorityQueue源码分析
问题 (1)什么是优先级队列? (2)怎么实现一个优先级队列? (3)PriorityQueue是线程安全的吗? (4)PriorityQueue就有序的吗? 简介 优先级队列,是0个或多个元素的集合 ...
- 死磕 java集合之CopyOnWriteArraySet源码分析——内含巧妙设计
问题 (1)CopyOnWriteArraySet是用Map实现的吗? (2)CopyOnWriteArraySet是有序的吗? (3)CopyOnWriteArraySet是并发安全的吗? (4)C ...
- 死磕 java集合之LinkedHashSet源码分析
问题 (1)LinkedHashSet的底层使用什么存储元素? (2)LinkedHashSet与HashSet有什么不同? (3)LinkedHashSet是有序的吗? (4)LinkedHashS ...
- 死磕 java集合之ArrayDeque源码分析
问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...
随机推荐
- 从零开始写一个前端脚手架四、初始化进程提示(chalk)
我们之前说过bin里面的index.js文件是作为入口文件存在的.实际上的初始化内容在.action里面操作的,为了方便管理,我们把实际操作的代码抽出来放一块儿管理 创建指令文件 在根目录创建一个co ...
- 『学了就忘』Linux系统管理 — 86、查看系统资源相关命令
目录 1.vmstat命令 2.dmesg命令 3.free命令 4.查看CPU信息 5.查看本机登陆用户信息 (1)w命令 (2)who命令 6.uptime命令 7.查看系统与内核相关信息 1.v ...
- Blazor中的无状态组件
声明:本文将RenderFragment称之为组件DOM树或者是组件DOM节点,将*.razor称之为组件. 1. 什么是无状态组件 如果了解React,那就应该清楚,React中存在着一种组件,它只 ...
- java 图形化小工具Abstract Window Toolit 菜单项
AWT 中的菜单由如下几个类组合而成 MenuBar: 菜单条,菜单的容器. Menu: 菜单组件,菜单项的容器,它也是Menultem的子类,所以可作为菜单项使用. PopupMenu: 上下文菜单 ...
- 用相对路径有时居然是这样,,加上<%=basePath%>
用相对路径有时居然是这样,所以还是用绝对路径好点,加上<%=basePath%> 比如create页面的action为ssh/pages/User/create,那么create页面的上的 ...
- libevent源码学习(6):事件处理基础——event_base的创建
目录前言创建默认的event_baseevent_base的配置event_config结构体创建自定义event_base--event_base_new_with_config禁用(避免使用)某一 ...
- navicat模型分享方法
一. 查看模型保存路径选中模型如:<app-订单模型>,点击右键,对象信息,可以看到文件位置:C:\Users\Administrator\Documents\Navicat\Premiu ...
- uniapp+nvue实现仿微信App聊天应用 —— 成功实现好友聊天+语音视频通话功能
基于uniapp + nvue实现的uniapp仿微信App聊天应用 txim 实例项目,实现了以下功能. 1: 聊天会话管理 2: 好友列表 3: 文字.语音.视频.表情.位置等聊天消息收发 4: ...
- 解决Tomcat9打印台乱码问题
问题描述: Tomcat打印台.打印出来的字体全是乱码后的显示.影响视觉体验,不利于bug查找和错误排查.故寻找方法去修改. 解决方法: 1.找到目录 2.对日志参数进行修改 3.改动编码 4.修改成 ...
- 【LeetCode】5083. Occurrences After Bigram 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字符串分割遍历 日期 题目地址:https://le ...