Java 集合框架:Vector、Stack 的介绍、使用、原理与源码解析

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 015 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。当然,我也会在必要的时候进行相关技术深度的技术解读,相信即使是拥有多年 Java 开发经验的从业者和大佬们也会有所收获并找到乐趣。

Java 集合框架(Java Collections Framework)是 Java 标准库中的一个核心组件,它提供了一套用于处理数据集合的接口和类。作为其中的重要成员,Vector 和 Stack 在特定场景中扮演着关键角色。Vector 是一种同步的动态数组,实现了 List 接口,适用于需要线程安全的场景;而 Stack 是 Vector 的子类,提供了后进先出(LIFO)的数据结构操作。本文将对 Vector 和 Stack 进行全面的介绍,探讨它们的使用方法、工作原理以及源码实现,以帮助开发者深入理解和高效使用这些集合类。同时,通过源码解析,我们将揭示其内部机制,为优化和定制提供有力支持。


文章目录

      • 1、 Vector 与 Stack
        • 1.1、Vector 概述
        • 1.2、Stack 概述
      • 2、Vector 的具体实现原理
        • 2.1、Vector 底层的数据结构
        • 2.2、Vector 的扩容
        • 2.3、Vector 对线程安全的实现
      • 3、Stack 的具体实现原理
      • 4、Vector 与 Stack 的过时原因与替代类
        • 4.1、不推荐使用 Vector 来实现线程安全的原因
        • 4.2、Stack 过时的原因


1、 Vector 与 Stack

写在前面:在开始介绍 Vector 与 Stack 之前,我们首先应该了解的是 Vector 与 Stack 这两个类在如今的 Java 版本中都早已过时,在 Java 出于对向后兼容性的考虑,才没有删除。但是我们不会因此认为 Vector 与 Stack 的实现是没有必要了解了,因为二者依旧会偶尔出现在面试问题当中。

1.1、Vector 概述

Vector 与 ArrayList 一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问 ArrayList慢。

Vector 的思路和 ArrayList 基本是相同的,底层是数组保存元素,Vector 默认的容量是10,有一个增量系数,如果指定,那么每次都会增加一个系数的大小,否则就扩大一倍。

Vector 扩容的时候,其实就是数组的复制,其实还是比较耗时间的,所以,我们使用的时候应该尽量避免比较消耗时间的扩容操作。

Vector 和 ArrayList 最大的不同,是它是线程安全的,几乎每一个方法都加上了 Synchronize 关键字,所以它的效率相对也比较低一点。ArrayList 如果需要线程安全,可以使用 Collections.synchronizedList(new ArrayList(...)); 方法,获取一个线程安全的 List。

1.2、Stack 概述

Stack 是 Java 集合框架中的一个类,它继承自 Vector,因此它本质上也是一个线程安全的动态数组。但是,Stack 类被设计为了一个后进先出(LIFO, Last In First Out)的数据结构,通常被称为栈。

image-20240618133542754

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶;
  • 出栈:栈的删除操作叫做出栈。出数据在栈顶。

2、Vector 的具体实现原理

2.1、Vector 底层的数据结构

与 ArrayList 类似(基本一致),Vector 内部也使用了动态数组来存储元素。

package java.util;import ...public class Vector<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节...// 存储元素的数组  protected Object[] elementData;  // 当前向量中元素的数量  protected int elementCount;  // 容量增量,用于指定每次容量不足时增长的大小,如果小于等于0,则每次增长一倍  protected int capacityIncrement;     /**  * 构造一个具有指定初始容量和容量增量的空向量。  *  * @param initialCapacity 向量的初始容量  * @param capacityIncrement 向量的容量增量  * @throws IllegalArgumentException 如果指定的初始容量是负数  */  public Vector(int initialCapacity, int capacityIncrement) {  super();  if (initialCapacity < 0)  throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);  this.elementData = new Object[initialCapacity];  this.capacityIncrement = capacityIncrement;  }  /**  * 构造一个具有指定初始容量的空向量,其容量增量等于零。  *  * @param initialCapacity 向量的初始容量  * @throws IllegalArgumentException 如果指定的初始容量是负数  */  public Vector(int initialCapacity) {  this(initialCapacity, 0);  }  /**  * 构造一个空的向量,使其内部数据数组的大小为10,标准容量增量为零。  */  public Vector() {  this(10);  }  // 省略其他方法和实现细节...
}
2.2、Vector 的扩容

Vector 的扩容方式依旧与 ArrayList 相同,当元素数量超过当前容量时,Vector 会复制一个新的更大容量的数组。可能在扩容上的主要不同就是 Array 的一次扩容为原数组的 1.5 倍(int newCapacity = oldCapacity + (oldCapacity >> 1)) 而 Vector 的扩容在不指定 capacityIncrement 的值得情况下为 2 倍(int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); )。

public class Vector<E>  extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable {  // ... 其他成员变量和方法 ...  // 容量增量,用于指定每次容量不足时增长的大小,如果小于等于 0,则每次增长一倍  protected int capacityIncrement;     // ... 其他成员变量和方法 ...  /**  * 将指定的元素作为此向量的最后一个元素插入。  *  * @param obj 要添加到此向量的元素  * @throws NullPointerException 如果指定的元素为 {@code null} 并且此向量不允许 null 元素  * (由具体实现决定)  */  public synchronized void addElement(E obj) {  modCount++; // 修改计数器增加,表示结构已修改  ensureCapacityHelper(elementCount + 1); // 确保容量足够  elementData[elementCount++] = obj; // 在当前末尾添加元素,并更新元素计数  }  /**  * 辅助方法,确保此向量的容量至少为指定的最小容量。  * 如果当前容量小于最小容量,则增加容量。  *  * @param minCapacity 所需的最小容量  */  private void ensureCapacityHelper(int minCapacity) {  // overflow-conscious code(防止溢出的代码)  if (minCapacity - elementData.length > 0)  grow(minCapacity); // 如果最小容量大于当前容量,则增加容量  }  /**  * 增加此向量的容量,以确保其至少能容纳指定的最小容量。  * 如果指定的最小容量大于当前容量,则此向量的容量将增加至大于或等于最小容量的最小可能值。  *  * @param minCapacity 所需的最小容量  */  private void grow(int minCapacity) {  // overflow-conscious code(防止溢出的代码)  int oldCapacity = elementData.length;  // 根据 capacityIncrement(如果大于0)或当前容量来计算新容量  int newCapacity = oldCapacity + ((capacityIncrement > 0) ?  capacityIncrement : oldCapacity);  // 如果新容量仍然小于最小容量,则直接使用最小容量  if (newCapacity - minCapacity < 0)  newCapacity = minCapacity;  // 检查新容量是否超过 MAX_ARRAY_SIZE,如果是,则调用 hugeCapacity 方法获取适当的大小  if (newCapacity - MAX_ARRAY_SIZE > 0)  newCapacity = hugeCapacity(minCapacity);  // 使用新的容量复制数组  elementData = Arrays.copyOf(elementData, newCapacity);  }  // ... 其他成员变量和方法 ...  }
2.3、Vector 对线程安全的实现

Vector 类和 ArrayList 类的最大区别在于线程安全性。Vector 类是线程安全的,可以在多线程环境中使用,但是性能相对较低。而 ArrayList 类不是线程安全的,性能相对较高。

而 Vector 的线程安全性的实现方式就是在与 Array 基本相同的方法之前加 synchronized 关键字。

public class Vector<E>  extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable {  // ... 其他成员变量和方法 ...  public synchronized E get(int index) {...}public synchronized E set(int index, E element) {...}public boolean remove(Object o) {...}public void add(int index, E element) {...}// ... 其他成员变量和方法 ...  }

3、Stack 的具体实现原理

Stack 的具体实现十分简单“

/**  * Stack 类表示后进先出(LIFO)的对象堆栈。  * 它继承自 Vector 类,提供了堆栈的一些基本操作,如 push、pop、peek 等。  *  * @param <E> 堆栈中元素的类型  */  
public class Stack<E> extends Vector<E> {  /**  * 无参构造函数,创建一个空的 Stack 实例。  */  public Stack() {  }  /**  * 将指定的元素推入此堆栈顶部。  *  * @param item 要推入堆栈顶部的元素  * @return 被推入堆栈的元素  */  public E push(E item) {  addElement(item);  return item;  }  /**  * 从此堆栈中弹出顶部对象,并返回该对象作为此函数的值。  *  * @return 堆栈顶部的元素(即最后一个添加的元素)  * @throws EmptyStackException 如果此堆栈为空  */  public synchronized E pop() {  E obj;  int len = size();  obj = peek();  removeElementAt(len - 1);  return obj;  }  /**  * 查看堆栈顶部的对象,但不从堆栈中移除它。  *  * @return 堆栈顶部的元素(即最后一个添加的元素)  * @throws EmptyStackException 如果此堆栈为空  */  public synchronized E peek() {  int len = size();  if (len == 0)  throw new EmptyStackException();  return elementAt(len - 1);  }  /**  * 测试此堆栈是否为空。  *  * @return 如果此堆栈不包含任何项,则返回 true;否则返回 false  */  public boolean empty() {  return size() == 0;  }  /**  * 返回此堆栈中最后一次出现的指定元素的索引(从 1 开始),  * 如果堆栈不包含此元素,则返回 -1。  *  * @param o 要搜索的元素  * @return 最后一次出现指定元素的索引(从 1 开始),如果未找到则返回 -1  */  public synchronized int search(Object o) {  int i = lastIndexOf(o);  if (i >= 0) {  return size() - i;  }  return -1;  }  // 序列化 ID 用于版本控制  private static final long serialVersionUID = 1224463164541339165L;  
}

4、Vector 与 Stack 的过时原因与替代类

4.1、不推荐使用 Vector 来实现线程安全的原因

在 Java 中,不推荐使用 Vector 来实现线程安全的 ArrayList,主要原因有以下几点:

  • 同步机制效率低下:Vector 的所有方法都被同步(synchronized)了,这意味着每次对 Vector 的操作都会获取并释放锁。这种方式会导致大量的上下文切换和锁竞争,影响性能。特别是在高并发环境下,这种同步机制会成为瓶颈。

  • 过时的设计:Vector 是 Java 1.0 引入的类,当时并没有考虑到并发编程的高效实现。后来,Java 引入了更高效的并发集合类,如 java.util.concurrent 包下的集合类,这些类在设计时充分考虑了并发环境下的性能问题。

  • 更好的替代品:Java引入了 Collections.synchronizedList 方法,可以将普通的 ArrayList 包装成线程安全的集合。此外,java.util.concurrent 包下提供了更现代化的并发集合类,如 CopyOnWriteArrayList,它们在并发场景下表现更好。

一些替代Vector的推荐方法和类:

  • 使用 Collections.synchronizedList:可以将一个普通的ArrayList转换为线程安全的集合
  • 使用 CopyOnWriteArrayListCopyOnWriteArrayListjava.util.concurrent包提供的线程安全的列表实现,它在写操作时进行复制,因此在并发读多写少的场景下性能较好
4.2、Stack 过时的原因

同样的,Java 中的 Stack 类已经被标记为过时(deprecated)。从 Java 1.2 版本开始,Stack 类就不再被推荐使用。Java 官方建议使用 Deque(双端队列)来替代 Stack,因为 Deque 提供了与 Stack 类似的操作方法,如 push()pop()peek()isEmpty() 等,同时还具有更灵活和高效的特点。

具体来说,Deque是一个接口,它支持在两端插入和删除元素,因此可以很方便地实现栈(Stack)的功能。而且,Deque接口的实现类(如LinkedList)在性能上通常优于Stack类。

以下是关于 Stack 类过时的一些关键点和原因:

  1. 历史遗留:Stack 类作为 Java 早期版本中的一个类,虽然提供了栈的基本功能,但随着 Java 集合框架的不断发展,更先进、更灵活的数据结构被引入,使得 Stack 类逐渐失去了其优势地位。
  2. 功能限制:Stack 类在功能上存在一些限制,比如它不支持并发访问,这在多线程编程中可能会导致问题。此外,Stack类的继承结构也使其在某些场景下使用不够灵活。
  3. 性能考虑:与 Deque 等现代数据结构相比,Stack 类在性能上可能存在不足。例如,Stack 类在插入和删除元素时可能需要进行额外的操作,从而降低了其效率。
  4. 替代方案:Java 提供了多种替代 Stack 类的方案,其中最常用的是 Deque 接口的实现类(如 LinkedList)。这些替代方案不仅提供了与 Stack 类类似的功能,而且在性能、灵活性和可扩展性方面都优于 Stack类。

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

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

相关文章

设计模式——观察者模式(发布/订阅模式)

观察者模式(发布/订阅模式) 是一种行为模式&#xff0c;允许你定义一种订阅机制&#xff0c;可在对象事件发生时通知多个“观察”该对象的其他对象 观察者模式定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时&am…

[机器学习算法]支持向量机

支持向量机&#xff08;SVM&#xff09;是一种用于分类和回归分析的监督学习模型。SVM通过找到一个超平面来将数据点分开&#xff0c;从而实现分类。 1. 理解基本概念和理论&#xff1a; 超平面&#xff08;Hyperplane&#xff09;&#xff1a;在高维空间中&#xff0c;将数据…

接口提示信息国际化, 调用LibreTranslate 离线翻译, 国际化支持

文章目录 背景实现方式步骤下载并部署离线翻译服务;前端接入 背景 将接口返回内容进行翻译, 以适配多语言需求; 实现方式 前端拦截接口返回内容, 调用离线翻译服务进行翻译, 翻译之后再进行相应的提示 参考资料: 离线翻译服务: https://github.com/LibreTranslate/LibreTra…

Revo Uninstaller Pro:专业卸载工具,为您的电脑系统深度清洁

Revo Uninstaller Pro 是一款功能强大的专业卸载工具&#xff0c;为用户提供了全面而高效的软件卸载解决方案。 在日常使用电脑的过程中&#xff0c;我们经常会遇到软件卸载不干净的问题&#xff0c;残留的文件和注册表项不仅占用宝贵的磁盘空间&#xff0c;还可能影响系统的稳…

36.6K star!Immich - 一款开源高性能的自托管照片和视频备份方案

大家好&#xff0c;今天给大家分享的是一个高性能的自托管照片和视频备份方案。 Immich 是一个图片管理和分享平台&#xff0c;它允许用户高效地组织、存储和访问他们的照片和视频集合。这个项目特别设计来优化个人和家庭的多媒体内容管理体验&#xff0c;提供了诸如自动备份、…

python编程笔记

python import库失败&#xff1a;原因是解释器选择不对&#xff0c;pip3 install numpy是把numpy库安装在3.xxx的python解释器中&#xff0c;但是我是在3.9.6的解释器下运行的&#xff0c;所以找不到&#xff0c;此时需要把解释器换成3.11.9即可。 Super(Net,self)._init_() 这…

am62x芯片安全类型确认(HS-SE, HS-FS or GP)

文章目录 芯片安全类型设置启动方式获取串口信息下载脚本运行脚本示例sk-am62x板卡参考芯片安全类型 AM62x 芯片有三个安全级别。 • GP:通用版本 • HS-FS:高安全性 - 现场安全型 • HS-SE:高安全性 - 强制安全型 在SD卡启动文件中,可以查看到, 但板上的芯片,到底是那…

Flutter-无限循环滚动标签

1. 序章 在现代移动应用开发中&#xff0c;滑动视图是常见的交互模式之一。特别是当你需要展示大量内容时&#xff0c;使用自动滚动的滑动视图可以显著提升用户体验。在这篇文章中&#xff0c;我们将讨论如何使用 Flutter 实现一个自动滚动的列表视图。 2. 效果 3. 实现思路 …

Nvidia Isaac Sim 入门教程 2024(3)图形界面

Isaac Sim 基本使用 版权信息 Copyright 2023-2024 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. The author …

项目二 OpenStack快速入门

任务一 熟悉OpenStack图形界面操作 1.1 Horizon项目 •各OpenStack服务的图形界面都是由Horizon提供的。 •Horizon提供基于Web的模块化用户界面。 •Horizon为云管理员提供一个整体的视图。 •Horizon为终端用户提供一个自主服务的门户。 •Horizon由云管理员进行管理…

OCC介绍及框架分析

1.OCC介绍 Open CASCADE &#xff08;简称OCC&#xff09;是一开源的几何造型引擎&#xff0c;OCCT库是由Open CASCADE公司开发和市场运作的。它是为开源社区比较成熟的基于BREP结构的建模引擎&#xff0c;能够满足二维三维实体造型和曲面造型&#xff0c;国内研究和使用它的单…

计算几何【Pick定理】

Pick 定理 Pick 定理&#xff1a;给定顶点均为整点的简单多边形&#xff0c;皮克定理说明了其面积 A {\displaystyle A} A 和内部格点数目 i {\displaystyle i} i、边上格点数目 b {\displaystyle b} b 的关系&#xff1a; A i b 2 − 1 {\displaystyle Ai{\frac {b}{2}}…

操作系统 文件系统

实验目的&#xff1a; 掌握文件系统设计的基本思想。理解掌握文件系统基本数据结构的设计。理解掌握文件操作中涉及的数据结构访问过程。 实验内容&#xff1a; 1、编程实现一个简单的内存文件系统。实现Linux常见的一些文件操作命令。比如&#xff1a;ls/cat/cp/rm等。 实…

链表经典面试题--链表修至圆满

目录 1.环形链表 a.为什么一定会相遇&#xff0c;有没有可能会错过&#xff0c;永远追不上&#xff1f;请证明 b.slow一次走1步&#xff0c;fast走3步 4步 5步 n步还一定追得上吗 请证明 2.环形链表2 3.随机链表的复制 1.环形链表 141. 环形链表 - 力扣&#xff08;Lee…

数学建模基础:数学建模概述

目录 前言 一、数学建模的步骤 二、模型的分类 三、模型评价指标 四、常见的数学建模方法 实际案例&#xff1a;线性回归建模 步骤 1&#xff1a;导入数据 步骤 2&#xff1a;数据预处理 步骤 3&#xff1a;建立线性回归模型 步骤 4&#xff1a;模型验证 步骤 5&…

进销存系统哪个好?2024最新进销存系统推荐(持续更新中)

现在市面上的进销存系统太多了&#xff0c;企业的选择越多&#xff0c;越容易踩坑。那企业在选择进销存系统时&#xff0c;需要考虑哪些因素&#xff1f; 1、系统的适配性 在进行选择之前&#xff0c;就应该先清楚自己的企业是重财务流管理、还是生产业务流管理、还是销售订单…

不是吧?爱上用AI绘画Stable Diffusion 出图的老板搞这出?打工人的时间真不多了

家人们&#xff01;AI留给职场打工人的时间真的不多了&#xff01; 有长远打算的老板们&#xff0c;已经开始研究AI了&#xff01; 甚至有设计师说&#xff0c;自己辛辛苦苦做的海报没过&#xff0c;AI做的却被老板通过了&#xff01;&#xff1f; 这两年&#xff0c;伴随着AI…

Unity3D 八叉树划分空间和可视化

也许更好的阅读体验 成果展示 代码 OctreeNode using System.Collections; using System.Collections.Generic; using UnityEngine; public class OctreeNode {//空间内包含的物体public List<GameObject> areaObjects;//空间中心public Vector3 center;//空间大小pub…

Sqlite3入门和c/c++下使用

1. SQLite3基本介绍 1.1 数据库的数据类型 1.2 基本语法 1. 创建数据表格 create table 表名(字段名 数据类型&#xff0c; 字段名 数据类型)&#xff1b; create table student(id int, name varchar(256), address text, QQ char(32)); 2. 插入数据 insert into 表名 valu…

工业制造领涉及的8大常见管理系统,如mes、scada、aps、wms等

在工业生产和制造领域有一些常见的管理系统&#xff0c;很多小伙伴分不清&#xff0c;这次大美B端工场带领大家了解清楚。 MES&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;&#xff1a; MES是一种用于监控、控制和优化生产过程的软件系统…