我们从一个DEMO作为入口,了解Java的Stack的源码,代码如:

 Stack<String> stack = new Stack<>();
 stack.push("a"); // 入栈
 stack.peek(); // 查询栈顶元素
 stack.pop(); // 出栈
 stack.empty(); // 判空
 stack.search("a"); // 搜索元素

Stack类继承自Vector,基于数组实现,如图:

主要包含5个方法:入栈、出栈、查询栈顶、判空、查找元素,每个方法都有sychronized同步锁来保持同步,所以是线程安全的我们自上而下打开每个方法

入栈push()

入栈操作会先判断是否有足够的空间来存储新元素,如果没有的话那么要进行扩容

 public E push(E item) {
     addElement(item); // 添加元素
     return item; // 返回当前元素
 }

 public synchronized void addElement(E obj) {
     modCount++; // 修改次数+1
     ensureCapacityHelper(elementCount + 1); // 确定数组容量是否足够,如果不够那么扩充数组
     elementData[elementCount++] = obj; // 栈顶索引+1,将当前元素赋值到栈顶
 }

 private void ensureCapacityHelper(int minCapacity) {
     if (minCapacity - elementData.length > 0) // 当前所需的容量 - 当前数组的容量 如果大于0,那么意味着超出了数组的容量
         grow(minCapacity); // 超出则扩充数组容量
 }

 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 数组元素很大的时候,可能会超出预想导致内存溢出,需要限定数组的最大值;这里-8的目的是因为有的虚拟机在实现的时候会在数组头部预留一些空间

 private void grow(int minCapacity) {
     int oldCapacity = elementData.length; // 当前数组的容量
     int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); // 计算新的容量,如果有指定增长那么 + 指定增长,如果没有指定那么当前容量乘以2
     if (newCapacity - minCapacity < 0) // 如果新容量还是不够
         newCapacity = minCapacity; // 使用所需容量为新容量
     if (newCapacity - MAX_ARRAY_SIZE > 0) // 如果新容量超过容量上限
         newCapacity = hugeCapacity(minCapacity);
     elementData = Arrays.copyOf(elementData, newCapacity); // 将原数组的数据拷贝到新的容器里,并替换原数组
 }

 private static int hugeCapacity(int minCapacity) {
     if (minCapacity < 0) // 超出INT最大值则会变为负数,这里抛出内存溢出异常
         throw new OutOfMemoryError();
     return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; // 所需容量大于上限,那么采用INT最大值,否则采用上限值
 } 

查询栈顶peek()

根据栈顶索引获取栈顶元素,但如果没有一个元素的情况下会抛出空栈异常

 public synchronized E peek() {
     int len = size(); // 当前栈的元素大小
     if (len == 0)  // 空栈抛出异常
         throw new EmptyStackException();
     return elementAt(len - 1); // 获取栈顶元素
 }

 public synchronized E elementAt(int index) {
     if (index >= elementCount) { // 栈顶索引大于等于元素大小表示越界
         throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
     }
     return elementData(index);
 }

 E elementData(int index) {
     return (E) elementData[index]; // 根据数组索引位置获取元素
 }

出栈pop()

出栈只是把栈顶的元素置为null,让GC去回收。出栈之前会先获取栈顶元素,所有如果对空栈出栈,那么会抛出异常

 public synchronized E pop() {
     E       obj;
     int     len = size(); // 获取当前元素大小
     obj = peek(); // 获取栈顶元素
     removeElementAt(len - 1); // 移除栈顶元素
     return 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); // 这里调用native方法,把被移除的元素之后的元素向前移动一位
     }
     elementCount--; // 元素数量 -1
     elementData[elementCount] = null; // 置为null,让GC去销毁该元素
 }

判空empty()

 public boolean empty() {
     return size() == 0; // 判断元素数量为0
 }

查找元素search()

查找元素的时候从栈顶向下查找,如果有多个那么只匹配最上面的一个,没有则返回-1

 public synchronized int search(Object o) {
     int i = lastIndexOf(o); // 从栈顶向下搜索,如果匹配到则返回自上而下的索引,否则返回-1
     if (i >= 0) {
         return size() - i; // 计算自下而上的索引
     }
     return -1; // 没有找到
 }

 public synchronized int lastIndexOf(Object o) {
     return lastIndexOf(o, elementCount-1); // 索引从栈顶开始
 }

 public synchronized int lastIndexOf(Object o, int index) {
     if (index >= elementCount) // 越界检验
         throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
     if (o == null) { // 如果元素为null
         for (int i = index; i >= 0; i--) // 自栈顶向下遍历
             if (elementData[i]==null) // 如果存在 == null的,返回
                 return i;
     } else { // 元素不为null
         for (int i = index; i >= 0; i--) // 自栈顶向下遍历
             if (o.equals(elementData[i])) // 元素相等的,返回
                 return i;
     }
     return -1; // 没有找到相等的元素
 }

Stack源码解析的更多相关文章

  1. Java - Stack源码解析

    Java提高篇(三一)-----Stack 在Java中Stack类表示后进先出(LIFO)的对象堆栈.栈是一种非常常见的数据结构,它采用典型的先进后出的操作方式完成的.每一个栈都包含一个栈顶,每次出 ...

  2. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  3. Java 集合系列 06 Stack详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. Java 集合系列Stack详细介绍(源码解析)和使用示例

    Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out). java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现 ...

  5. 【转】 Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  6. Java Stack源码分析

    Stack简介 Stack是栈.它的特性是:先进后出(FILO, First In Last Out).java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现的 ...

  7. 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析

    前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈.      什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...

  8. 源码解析-Volley(转自codeKK)

    Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon ...

  9. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

随机推荐

  1. Git项目下载部分文件或文件夹

    我们常常要在Github下载一些源码.示例等,但有时候项目库会比较大,而我关心的只是其中很少的一部分内容,由于众所周知的原因,我们下载git库是比较慢的,过大的项目经常会下载失败,所以只下载部分内容就 ...

  2. Asp.Net Mvc异步上传文件的方式

    今天试了下mvc自带的ajax,发现上传文件时后端action接收不到文件, Request.Files和HttpPostedFileBase都接收不到.....后来搜索了下才知道mvc自带的Ajax ...

  3. 动态产生select option列表

    在很久之前,Insus.NET刚学习前端时,有写过<动态创建一些常的html标签>http://www.cnblogs.com/insus/p/3741665.html 但其中没有实现动态 ...

  4. 剑指offer编程题Java实现——面试题3二维数组中的查找

    题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.   下面是我实现的代码 ...

  5. vue+iview实现一行平均五列布局

    iview 的栅格布局是以 html代码部分: <Row :gutter="20"> <Col style="float: left;width: 20 ...

  6. 【xsy2913】 enos 动态dp

    题目大意:给你一棵 $n$个点 以 $1$为根 的树,每个点有$ 0,1,2 $三种颜色之一,初始时整棵树的颜色均为 $0$. $m$ 次操作, 每次操作形如: 1 x y c : 将 $x$到$y$ ...

  7. How To Scan QRCode For UWP (2)

    这篇随笔主要介绍照相预览功能,重要使用的是MediaCapture对象,MediaCapture对象还可以用来处理录音和录制视频,本文只讨论照相功能. 1:查找摄像头 后置摄像头优先,找不到后置摄像头 ...

  8. War文件部署

    其实,开始要求将源码压缩成War文件时,一头雾水! 公司项目要求做CAS SSO单点登录 也就是这玩意.... 其实war文件就是Java中web应用程序的打包.借用一个老兄的话,“当你一个web应用 ...

  9. centos 7 Mysql5.7 主从复制配置

    1.环境 Centos 7 Mysql 5.7 Master  192.168.1.71 Slave01 192.168.1.72 2.分别配置master,slave01 # vi /etc/my. ...

  10. [转]Android中handler机制的原理

    Andriod提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange). 1)Loo ...