Stack

如果我们去查jdk的文档,我们会发现stack是在Java.util这个包里。它对应的一个大致的类关系图如下:

通过继承Vector类,Stack类可以很容易的实现他本身的功能。因为大部分的功能在Vector里面已经提供支持了。

Stack里面主要实现的有一下几个方法:

方法名

返回类型

说明

empty

boolean

判断stack是否为空。

peek

E

返回栈顶端的元素。

pop

E

弹出栈顶的元素

push

E

将元素压入栈

search

int

返回最靠近顶端的目标元素到顶端的距离。

因为前面我们已经提到过,通过继承Vector,很大一部分功能的实现就由Vector涵盖了。Vector的详细实现我们会在后面分析。它实现了很多的辅助方法,给Stack的实现带来很大的便利。现在,我们按照自己的思路来分析每个方法的具体步骤,再和具体实现代码对比。

empty

从我们的思路来说,如果要判断stack是否为空,就需要有一个变量来计算当前栈的长度,如果该变量为0,则表示该栈为空。或者说我们有一个指向栈顶的变量,如果它开始的时候是设置为空的,我们可以认为栈为空。这部分的实现代码也很简单:

public booleanempty() {return size() == 0;

}

如果更进一步分析的话,是因为Vector已经实现了size()方法。在Vector里面有一个变量elementCount来表示容器里元素的个数。如果为0,则表示容器空。这部分在Vector里面的实现如下:

public synchronized intsize() {returnelementCount;

}

peek

peek是指的返回栈顶端的元素,我们对栈本身不做任何的改动。如果栈里有元素的话,我们就返回最顶端的那个。而该元素的索引为栈的长度。如果栈为空的话,则要抛出异常:

public synchronizedE peek() {int len =size();if (len == 0)throw newEmptyStackException();return elementAt(len - 1);

}

这个elementAt方法也是Vector里面的一个实现。在Vector里面,实际上是用一个elementData的Object数组来存储元素的。所以要找到顶端的元素无非就是访问栈最上面的那个索引。它的详细实现如下:

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

}returnelementData(index);

}

@SuppressWarnings("unchecked")

E elementData(intindex) {return(E) elementData[index];

}

pop

pop方法就是将栈顶的元素弹出来,如果栈里有元素,就取最顶端的那个,否则就要抛出异常:

public synchronizedE pop() {

E obj;int len =size();

obj=peek();

removeElementAt(len- 1);returnobj;

}

在这里,判断是否可以取栈顶元素在peek方法里实现了,也将如果栈为空则抛异常的部分包含在peek方法里面。这里有必要注意的一个细节就是,在通过peek()取到顶端的元素之后,我们需要用removeElementAt()方法将最顶端的元素移除。我们平时可能不太会留意到这一点。为什么要移除呢?我们反正有一个elementCount来记录栈的长度,不管它不是也可以吗?

实际上,这么做在程序运行的时候会有一个潜在的内存泄露的问题。因为在java里面,如果我们普通定义的类型属于强引用类型。比如这里vector就底层用的Object[]这个数组强类型来保存数据。强类型在jvm中做gc的时候,只要程序中有引用到它,它是不会被回收的。这就意味着在这里,只要我们一直在用着stack,那么stack里面所有关联的元素就都别想释放了。这样运行时间一长就会导致内存泄露的问题。那么,为了解决这个问题,这里就是用的removeElementAt()方法。

blic synchronized void removeElementAt(intindex) {

modCount++;if (index >=elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);

}else if (index < 0) {throw newArrayIndexOutOfBoundsException(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*/}

这个方法实现的思路也比较简单。就是用待删除元素的后面元素依次覆盖前面一个元素。这样,就相当于将数组的实际元素长度给缩短了。因为这里这个移除元素的方法是定义在vector中间,它所面对的是一个更加普遍的情况,我们移除的元素不一定就是数组尾部的,所以才需要从后面依次覆盖。如果只是单纯对于一个栈的实现来说,我们完全可以直接将要删除的元素置为null就可以了。

push

push的操作也比较直观。我们只要将要入栈的元素放到数组的末尾,再将数组长度加1就可以了。

publicE push(E item) {

addElement(item);returnitem;

}

这里,addElement方法将后面的细节都封装了起来。如果我们更加深入的去考虑这个问题的话,我们会发现几个需要考虑的点。

1. 首先,数组不会是无穷大的 ,所以不可能无限制的让你添加元素下去。当我们数组长度到达一个最大值的时候,我们不能再添加了,就需要抛出异常来。

2. 如果当前的数组已经满了,实际上需要扩展数组的长度。常见的手法就是新建一个当前数组长度两倍的数组,再将当前数组的元素给拷贝过去。

前面讨论的这两点,都让vector把这份心给操了。我们就本着八卦到底的精神看看它到底是怎么干的吧:

public synchronized voidaddElement(E obj) {

modCount++;

ensureCapacityHelper(elementCount+ 1);

elementData[elementCount++] =obj;

}private void ensureCapacityHelper(intminCapacity) {//overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}private void grow(intminCapacity) {//overflow-conscious code

int oldCapacity =elementData.length;int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)

newCapacity=minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity=hugeCapacity(minCapacity);

elementData=Arrays.copyOf(elementData, newCapacity);

}private static int hugeCapacity(intminCapacity) {if (minCapacity < 0) //overflow

throw newOutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

看到这部分代码的时候,我不由得暗暗叹了口气。真的是拔了萝卜带出泥。本来想看看stack的细节实现,结果这些细节把vector都深深的出卖了。在vector中间有几个计数的变量,elementCount表示里面元素的个数,elementData是保存元素的数组。所以一般情况下数组不一定是满的,会存在着elementCount <= elementData.length这样的情况。这也就是为什么ensureCapacityHelper方法里要判断一下当新增加一个元素导致元素的数量超过数组长度了,我们要做一番调整。这个大的调整就在grow方法里展现了。

grow方法和我们所描述的方法有点不一样。他不一样的一点在于我们可以用一个capacityIncrement来指示调整数组长度的时候到底增加多少。默认的情况下相当于数组长度翻倍,如果设置了这个变量就增加这个变量指定的这么多。

search

search这部分就相当于找到一个最靠近栈顶端的匹配元素,然后返回这个元素到栈顶的距离。

public synchronized intsearch(Object o) {int i =lastIndexOf(o);if (i >= 0) {return size() -i;

}return -1;

}

对应在vector里面的实现也相对容易理解:

public synchronized intlastIndexOf(Object o) {return lastIndexOf(o, elementCount-1);

}public synchronized int lastIndexOf(Object o, intindex) {if (index >=elementCount)throw new IndexOutOfBoundsException(index + " >= "+elementCount);if (o == null) {for (int i = index; i >= 0; i--)if (elementData[i]==null)returni;

}else{for (int i = index; i >= 0; i--)if(o.equals(elementData[i]))returni;

}return -1;

}

这个lastIndexOf的实现无非是从数组的末端往前遍历,如果找到这个对象就返回。如果到头了,还找不到对象呢?...不好意思,谁让你找不到对象的?活该你光棍,那就返回个-1吧。

Vector

在前面对stack的讨论和分析中,我们几乎也把vector这部分主要的功能以及实现给涵盖了。vector和相关类以及接口的关系类图如下:

因为Java没有内置对List类型的支持,所以Vector内部的实现是采用一个object的array。其定义如下:

protected Object[] elementData;

这里从某种角度来说可以说是java里对泛型支持的不足,因为内部保存数据的是Object[],在存取数据的时候如果不注意的话会出现存取数据类型不一致的错误。所以在以下的某些个方法里需要加上@SuppressWarnings("unchecked")的声明。

@SuppressWarnings("unchecked")

E elementData(intindex) {return(E) elementData[index];

}

我们前面讨论的那些数组的增长,删除元素,查找元素以及修改等功能就占据了vector的大部分。如果有兴趣看vector的源代码的话,会发现里面主要就是这些功能的实现再加上一个迭代器功能。总共的代码不是很多,1200多行,这里就不再赘述了。

可以说,vector它本身就是一个可以动态增长的数组。和我们常用的ArrayList很像。和ArrayList的不同在于它对元素的访问都用synchronized修饰,也就是说它是线程安全的。在多线程的环境下,我们可以使用它。

java int stack_java stack总结相关推荐

  1. java int stack_java Stack的使用

    堆栈是一种 "后进先出"  (LIFO) 的数据结构, 只能在一端进行插入(称为 "压栈" ) 或删除 (称为"出栈")数据的操作.所以很适 ...

  2. java int stack_java中int算法的有趣现象

    今天无意中发现一个怪事,当时没理解,后来跟网友讨论了才知道原理,是关于int值的加法算法,两段代码如下: 代码1: @Testpublic voidtest1() {int stackLength = ...

  3. java中的stack类和C++中的stack类的区别

    文章目录 1 java中的stack类和C++中的stack类的区别 1.1 java中的stack类 1.2 C++中的stack类 1.3 分析 不经意间想到了这个问题,存到栈中的是对象的引用,还 ...

  4. java中堆栈(stack)和堆(heap)

    http://www.ej38.com/showinfo/java-172156.html 堆栈是一种先进后出的数据结构,只能在一端进行输入或输出数据的操作  Stack类在java.util包中 向 ...

  5. java int不将0忽略_Java微服务:蛋糕是骗人的,但您不能忽略它

    java int不将0忽略 构建微服务实际上意味着什么? 通过微服务框架的眼光回答 忽略微服务的趋势已变得不可能. 有些人会说这只是另一个难以忍受的流行语,而另一些人会背诵打破巨石的优势或采取逆势方法 ...

  6. java为什么不推荐使用stack_栈和队列的面试题Java实现,Stack类继承于Vector这两个类都不推荐使用...

    在 thinking in java中看到过说Stack类继承于Vector,而这两个类都不推荐使用了,但是在做一到OJ题时,我用LinkedList来模拟栈和直接用Stack,发现在进行入栈出栈操作 ...

  7. Java集合之Stack(出自Java知识体系)

    安琪拉正在梳理Java知识体系,这篇讲Java集合的Stack.如果希望获取完整的<安琪拉Java知识体系>整理中, 完成后公众号回复"知识体系" 即可获取. 完整的J ...

  8. java int ==_Java 位运算符和 int 类型的实现

    其他运算符 # 算术运算符 +.-.*./.++i.i++.--i.i-- # 关系运算符 ==.!=.>.=.<= # 逻辑运算符 &&.||.! # 赋值运算符 =.运 ...

  9. Java String到int,Java int到String

    Today we will look at Java String to int conversion and then java int to String conversion. Java pro ...

  10. java int溢出,结果只会保留低32位,高位会抛弃掉

    今天做leetcode题目时,发现int mid = 536848900,但是mid*mid=484528144,我说为啥程序运行一直出错呢!! int mid = 536848900;System. ...

最新文章

  1. python全栈开发笔记---------函数
  2. c++输出的值精确到小数点后5位_七年级上册第一章有理数专讲专练(适合暑期预习后巩固)...
  3. UNIX再学习 -- TCP/UDP 客户机/服务器
  4. python中的然后_返回,然后等待另一个函数在python中完成
  5. 再获全球顶会ASPLOS认可:阿里云神龙凭什么打破物理机神话
  6. 关于人生的十句经典的话! .
  7. [Java] 蓝桥杯ALGO-27 算法训练 FBI树
  8. python flask快速入门与进阶-Flask基础进阶与python flask实战bbs教程
  9. 阿里云前端周刊 - 第 25 期
  10. ios实现图片动画效果
  11. 《监控》其实是讲一个年轻人彻底社会化的过程
  12. uygurqa输入法android,uygurqaapp输入法
  13. linux u盘 随身,教你安装CentOS到U盘,制作随身Linux系统
  14. 深度思考:广州互联网气氛为何远远落后于北京
  15. ROS创建Web代理(Web proxy)给QQ使用HTTP代理
  16. 问题 J: Frosting on the Cake
  17. com.zxy.android tiny,遇到一个 SIGABRT 这样的错误
  18. 艾永亮:时代的较量,谁在定义四个轮子的未来?
  19. 中间服务器代理解决跨域
  20. 【java】删除文件夹及文件夹中的所有文件

热门文章

  1. 【优化算法】先导粘菌算法(LSMA)【含Matlab源码 1436期】
  2. 【带钢厚度预测】基于matlab GUI SVM带钢厚度预测【含Matlab源码 173期】
  3. 【优化布局】基于matlab遗传算法求解带出入点的车间布局优化问题【含Matlab源码 011期】
  4. 【基础教程】基于matlab工具voicebox函数中文说明【含Matlab源码 032期】
  5. java多线程实现医院叫号_Java多线程经典题目(医院挂号)
  6. 技术专家(ai/大数据)_``我们淹没在数据中'':在专家和AI时代如何思考自己
  7. 360无痕浏览器_功能强大好用的浏览器
  8. c语言注释部分两侧的分界符号分别是,C语言习题及解答.doc
  9. 端口扫描php,php-批量端口扫描
  10. 那些让程序员炸毛的奇葩需求,说起来满满的都是泪!