当前位置: 首页 > 后端技术 > Java

Java集合[13]————Stack源码分析一波

时间:2023-04-01 14:00:49 Java

前言集合源码分析系列:Java集合源码分析之前已经分析过Vector、ArrayList、LinkedList,本来想入手Map的,但是看了下面的本界面设计框架图:整个界面框架关系如下(来自百度百科):还是漏网之鱼。Stack挂在Vector下。之前我们已经分析过Vector,那么我们顺便分析一下Stack。不写就2022年了:Stack简介Stack是一种数据结构,不是Java独有的,在Java中体现在Stack类中。它的本质是先进后出,就像一个水桶,只能不断的往上面放,取出来的时候,只能连续取出最上面的数据。如果要取出底层数据,只有把上面的数据都取出来才可以。当然,如果有这样的需求,我们一般使用双向队列。下面是stack特性的演示:Stack在Java中继承自Vector,这里是1.8版本,共享了Vector的底层数据结构。底层数据使用数组实现,具有以下特点:)继承自Vector,也是基于数组实现的。由于几乎所有的Vector都用到,Vector的操作是线程安全的,所以Stack的操作也是线程安全的。类定义源码:publicclassStackextendsVector{}成员变量只有一个序列化变量:privatestaticfinallongserialVersionUID=1224463164541339165L;方法解释Stack本身并没有太多的方法,几乎??都是继承自Vector,我们可以看一下它自己扩展的5个方法:Epush(Eitem):pushthestacksynchronizedEpop():popthestacksynchronizedEpeek():获取栈顶元素booleanempty():判断栈是否为空intsearch(Objecto):在栈中搜索一个对象的索引位置。push方法其实调用了最底层的addElement()方法,也就是Vector的方法:publicEpush(Eitem){addElement(item);归还物品;}我们之前分析过Vecor的源码,有兴趣的可以参考:http://aphysia.cn/archives/ja...addElement是线程安全的,在底层其实是加了一个元素,但它可以帮助我们保证容量。如果容量不足,会触发自动扩容机制。publicsynchronizedvoidaddElement(Eobj){modCount++;ensureCapacityHelper(elementCount+1);元素数据[元素计数++]=obj;}pop方法底层是先调用peek()方法获取栈顶元素,然后调用removeElementAt()方法移除栈顶元素,实现栈顶元素。publicsynchronizedEpop(){Eobj;intlen=size();obj=偷看();removeElementAt(len-1);返回对象;}removeElementAt(intindex)也是一个Vector方法,同步修改,线程安全的,由于移除了数组的最后一个元素,所以这里不会触发元素复制,即System.arraycopy(elementData,index+1,elementData,index,j);:publicsynchronizedvoidremoveElementAt(intindex){modCount++;if(index>=elementCount){thrownewArrayIndexOutOfBoundsException(index+">="+elementCount);}}elseif(index<0){thrownewArrayIndexOutOfBoundsException(index);}intj=elementCount-索引-1;如果(j>0){System.arraycopy(elementData,index+1,elementData,index,j);}元素计数--;元素数据[元素计数]=null;/*让gc干活*/}peek方法获取栈顶元素,先获取数组大小,然后调用Vector的EelementAt(intindex)获取该索引处的元素:publicsynchronizedEpeek(){intlen=size();如果(len==0)抛出新的EmptyStackException();返回elementAt(len-1);}EelementAt(intindex)的源码如下,里面的逻辑比较简单,只有数组越界的判断:publicsynchronizedEelementAt(intindex){if(index>=elementCount){thrownewArrayIndexOutOfBoundsException(index+">="+elementCount);}返回元素数据(索引);}empty方法主要用来判断元素栈中是否有元素。主要方法是调用size()方法:publicbooleanempty(){returnsize()==0;}这个size()方法其实是一个Vector方法,它返回的其实是一个类变量,元素个数:publicsynchronizedintsize(){returnelementCount;}search方法该方法主要用于查询一个元素的索引,如果有多个,则返回最后一个元素的索引:publicsynchronizedintsearch(Objecto){inti=lastIndexOf(o);如果(i>=0){返回大小()-i;}返回-1;}使用synchronized修改也是线程安全的,为什么需要这个方法呢?我们知道栈是先进先出的,如果需要在里面找元素position,那么在判断之前一个一个取出来太麻烦了,而且底层使用数组存储,可以直接利用这个特性快速找到元素的索引位置至此,回过头来看,是不是觉得疑惑,`Stack里面没有数据,却在不停地操作数据,这得益于Vector的数据结构://TheunderlyingarrayprotectedObject[]elementData;//受保护的元素个数intelementCount;数组底层是用来保存元素个数的,如果元素个数确定了,那么操作元素其实就是不断往数组中插入元素,或者取出最后一个元素。总结Stack也是线程安全的,因为它继承自Vector。底层使用数组保存数据,大部分API使用Vector保存。它最重要的特性是先进先出。至于数组展开,则遵循Vector中的展开逻辑。如果让我们自己实现的话,底层不一定要用数组,同样的功能可以用链表来实现,但是在整个集合源码系统中共享相同的部分是一个不错的选择.【作者简介】:公众号【秦淮杂货铺】作者秦淮,个人网站:http://aphysia.cn,技术之路非一蹴而就,山高水长江河漫漫,纵然缓慢,也不会停下脚步。剑指全题OfferPDF开源编程笔记