当前位置: 首页 > java >正文

[数据结构] ArrayList 与 顺序表

一.线性表

  • 线性表是 n 个具有相同特性的数据元素的有限序列
  • 线性表是 一种在实际中广泛使用的数据结构 
  • 常见的线性表 : 顺序表 , 链表 , 栈 , 队列

线性表在逻辑上是 线性结构 (一条直线) ; 但是在物理结构上并不一定是连续的 , 线性表在物理上存储时 , 通常以数组和链式结构的形式存储


二.顺序表

顺序表是 一段物理地址连续 的存储单元依次存储数据元素的线性结构 ,一般情况下采用数组存储; 在数据上完成数据的增删查改

1.模拟实现

①接口的实现

public interface IList /*<E>*/{// 新增元素,默认在数组最后新增public void add(int data);boolean isFull();// 在 pos 位置新增元素public void add(int pos, int data) ;// 判定是否包含某个元素public boolean contains(int toFind);// 查找某个元素对应的位置public int indexOf(int toFind);// 获取 pos 位置的元素public int get(int pos);// 给 pos 位置的元素设为 valuepublic void set(int pos, int value);//删除第一次出现的关键字keypublic void remove(int toRemove);// 获取顺序表长度public int size() ;// 清空顺序表public void clear();// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的public void display() ;
}

②功能实现

import java.util.Arrays;public class MyArrayList implements IList {public int[] array;public int usedSize;public int DEFAULT_CAPACITY = 10;public MyArrayList() {this.array = new int[DEFAULT_CAPACITY];}public void grow(){this.array = Arrays.copyOf(this.array,array.length*2);//扩容}public boolean isFull() {return this.usedSize == array.length;//判满}// 新增元素,默认在数组最后新增@Overridepublic void add(int data)  {if(isFull()){//判满,满了就扩容grow();}this.array[usedSize] = data;usedSize++;}public void Checkpos1 (int pos)throws posException{//判断下标是否在(0,pos)位置if(pos<0||pos>usedSize){throw new posException("下标异常");}}// 在 pos 位置新增元素@Overridepublic void add(int pos, int data) {try {if (isFull()) {//判满,满了就扩容grow();}Checkpos1(data);//①(0,pos)位置添加元素 , 需要挪开该下标的元素 , 再添加;②pos位置 , 同add(int data) , 在末尾插入for (int i = this.usedSize-1; i >= pos ; i--) {array[i+1] = array[i];}array[pos] = data;usedSize++;}catch (posException e){System.out.println("下标不合法");e.printStackTrace();}}// 判定是否包含某个元素@Overridepublic boolean contains(int toFind) {for (int i = 0; i < this.usedSize; i++) {//遍历this.array[i] = toFind;return true;//找到返回true}return false;}// 查找某个元素对应的位置@Overridepublic int indexOf(int toFind) {for (int i = 0; i < this.usedSize; i++) {//遍历this.array[i] = toFind;return i;//找到返回下标}return -1;}public void Checkpos2 (int pos)throws posException{if(pos<0||pos>=usedSize){throw new posException("下标异常");//和Checkpos1不同的是 , 查找的范围是(0,usedSize)并不包含usedSize}}public void isEpty()throws emptyException{//判断是否为空if(this.array.length == 0){throw new emptyException();}}// 获取 pos 位置的元素@Overridepublic int get(int pos) {try {Checkpos2(pos);//判断下标是否合法 ,和Checkpos1不同的是 , 查找的范围是(0,usedSize-1]并不包含usedSizeisEpty();//判断是否为空return array[pos];}catch (posException e) {System.out.println("下标不合法");e.printStackTrace();} catch (emptyException e) {System.out.println("数组为空数组");e.printStackTrace();}return -1;}// 给 pos 位置的元素设为 value ; 思路和get(int pos)类似@Overridepublic void set(int pos, int value) {try {Checkpos2(pos);//判断下标是否合法 ,和Checkpos1不同的是 , 查找的范围是(0,usedSize-1]并不包含usedSizeisEpty();//判断是否为空array[pos] = value;}catch (posException e) {System.out.println("下标不合法");e.printStackTrace();} catch (emptyException e) {System.out.println("数组为空数组");e.printStackTrace();}}//删除第一次出现的关键字key@Overridepublic void remove(int toRemove) {try{isEpty();//判断是否为空int pos = indexOf(toRemove);//用indexOf(int toFind)(在上面)来查找if(pos == -1) {//该情况是没有找到return;}for (int i = pos; i < this.usedSize; i++) {//补齐空缺的位置array[i] = array[i+1];}usedSize--;//下方代码思路一样
//            for(int pos = 0; pos < this.usedSize; pos++){//从0下标开始找toRemove
//                if(array[pos] == toRemove){
//                    for (int i = pos; i < this.usedSize; i++) {
//                        array[i] = array[i+1];
//                    }
//                }
//            }
//            usedSize--;}catch (emptyException e){System.out.println("数组为空");e.printStackTrace();}}// 获取顺序表长度@Overridepublic int size() {try {isEpty();return this.usedSize;//有效长度}catch (emptyException e){System.out.println("数组为空");e.printStackTrace();}return 0;}// 清空顺序表@Overridepublic void clear() {/* for (int i = 0; i < usedSize; i++) {array[i] = null;//将所有元素置为null}*/usedSize = 0;//将usedSize设置成0,下一次默认新增元素 则在0下标}// 打印顺序表@Overridepublic void display() {for (int i = 0; i < this.usedSize; i++) {System.out.print(array[i] + " ");}}
}

③异常

public class emptyException extends Exception{public emptyException() {}public emptyException(String message) {super(message);}
}
public class posException extends Exception {public posException() {}public posException(String message) {super(message);}
}


三.ArrayList简介

说明:

  • ArrayList 是以泛型方式实现的,使用时必须要先实例化
  • Arraylist 实现了Randomaccess接口,表明ArrayList支持随机访问
  • ArrayList 实现了Cloneable接口,表明ArrayList是可以被clone的
  • ArrayList 实现了Serializable接口,表明ArrayList是支持序列化的
  • 和Vector不同,ArrayList 不是现成安全的,在单线程下可以使用,在多线程中 可以使用 Vector 或者 CopyOnWriteArrayList
  • ArrayList 底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表


四.ArrayList使用

1.ArrayList 构造

代码演示 :

public static void main(String[] args) {//无参构造 构造一个空的列表List<Integer> list1 = new ArrayList<>();list1.add(1);list1.add(3);//构造一个具有10个容量的列表List<Integer> list2 = new ArrayList<>(10);list2.add(5);list2.add(6);//List3 中的内容与list2一致List<Integer> list3 = new ArrayList<>(list2);//list4类型未指定 任意元素都可存放List list4 = new ArrayList<>();list4.add("123");list4.add(123);}

2.ArrayList 常见操作

方法解释
boolean add(E e)尾插 e
void add(int index , E element)将 e 插入index 位置
boolean addAll(Collection<? extendsE>c)尾插 c 中的元素
E remove (int index)删除 index 位置的元素
boolean remove(Object o)删除遇到的第一个 o
E get(int indxt)获取下标 index 位置的元素
E set(int index , E element)将 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标

List <E> subList(int fromIndex,

int toIndex)

截取部分 list
    public static void main(String[] args) {List<String> list = new ArrayList<>();//尾插元素list.add("JavaSE");list.add("JavaWeb");list.add("JavaEE");list.add("JVM");list.add("Test");System.out.println(list);//[JavaSE, JavaWeb, JavaEE, JVM, Test]//在 list 的 index 位置插入指定元素 , index 及后续元素向后移动一个位置list.add(1,"JJava");System.out.println(list);//[JavaSE, JJava, JavaWeb, JavaEE, JVM, Test]//获取 list 中有效元素的个数System.out.println(list.size());//6//获取和设置 index 位置上的元素 ; index 介于[0,size)System.out.println(list.get(1));//JJavalist.set(1,"abc");System.out.println(list.get(1));//abc//在 list 的 index 位置插入指定元素 , index 及后续元素向后移动一个位置list.add(1,"JJava");System.out.println(list);//[JavaSE, JJava, abc, JavaWeb, JavaEE, JVM, Test]//删除指定元素,找到就删除,并将该元素之后的元素统一向前移动一个位置list.remove("JVM");System.out.println(list);//[JavaSE, JJava, abc, JavaWeb, JavaEE, Test]//删除 list 中 index 位置的元素list.remove(list.size()-1);System.out.println(list);//[JavaSE, JJava, abc, JavaWeb, JavaEE]//检测 list 中是否包含指定元素 , 包含返回true , 否则返回falseif(!list.contains("Test")){list.add("Test");}//查找指定元素第一次出现的位置 ; indexOf从前往后找 , lastIndexOf从后往前找System.out.println(list.indexOf("Test"));//5System.out.println(list.lastIndexOf("Test"));//5//使用list 中[0,3)之间的元素构成一个新的 SubList 返回//但是 和 ArrayList 共用一个 elementData 数组;即不会产生新的对象,在原数组上直接截取List<String> ret = list.subList(0,3);System.out.println(ret);//[JavaSE, JJava, abc]}

3.ArrayList 的打印 (遍历)

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器

public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);//使用下标 + for遍历for(int i = 0;i<list.size();i++){System.out.println(list.get(i));}//for each遍历for (Integer integer:list) {System.out.println(integer+" ");}System.out.println(list);//使用迭代器输出Iterator<Integer> it = list.iterator();while (it.hasNext()){System.out.println(it.next()+" ");}}
  • 使用下标+for遍历
        for(int i = 0;i<list.size();i++){System.out.println(list.get(i));}
  • 借助foreach遍历
        for (Integer integer:list) {System.out.println(integer+" ");}
  • 使用迭代器输出
        Iterator<Integer> it = list.iterator();while (it.hasNext()){System.out.println(it.next()+" ");}


五.ArrayList的扩容机制

  • ArrayList 是基于动态数组实现的集合类,其核心特点是支持自动扩容以容纳更多元素。扩容机制的设计平衡了内存使用和性能效率,避免频繁申请内存空间带来的开销

1.初始容量与扩容触发条件

默认初始容量为 10(无参构造时)。当调用 add() 方法添加元素且当前容量不足时触发扩容。扩容条件通过 ensureCapacityInternal() 方法检测,最终调用 grow() 方法执行扩容操作

2.扩容具体步骤

  • 扩容时,新容量计算遵循公式:newCapacity = oldCapacity + (oldCapacity >> 1)
  • 即新容量为旧容量的 1.5 倍(位运算右移一位等效于除以 2);例如原容量为 10,扩容后为 15
  • 若计算的新容量仍小于最小需求容量(如一次性添加大量元素),则直接采用需求容量作为新容量。新容量超过 MAX_ARRAY_SIZEInteger.MAX_VALUE - 8)时,会校验是否溢出并可能抛出 OutOfMemoryError

3.说明:

  •  检测是否真正需要扩容 ,如果是调用 grow 准备扩容
  •  预估需要库容的大小初步预估按照 1.5 倍大小扩容如果用户所需大小超过预估 1.5 倍大小 , 则按照用户所需大小扩容真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
  • 使用 copyOf 进行扩容

http://www.xdnf.cn/news/18148.html

相关文章:

  • 【前端进阶】UI渲染优化 - 骨架屏技术详解与多框架实现方案
  • RH134 管理网络安全知识点
  • CMake指令:查找文件(find_file)、查找目录(find_path)、查找库文件(find_library)
  • ANSI终端色彩控制知识散播(I):语法封装(Python)——《彩色终端》诗评
  • 图论Day5学习心得
  • 【运维进阶】LNMP + WordPress 自动化部署实验
  • 《Image Classification with Classic and Deep Learning Techniques》复现
  • [Code Analysis] docs | Web应用前端
  • 深度学习-计算机视觉-微调 Fine-tune
  • 学习游戏制作记录(各种独特物品效果)8.18
  • 机器学习-决策树:从原理到实战的机器学习入门指南
  • AI大模型实战:用自然语言处理技术高效处理日常琐事
  • 嵌入式设备Lwip协议栈实现功能
  • 【软考架构】第4章 密钥管理技术和访问控制及数字签名技术
  • 【前端】使用Vue3过程中遇到加载无效设置点击方法提示不存在的情况,原来是少加了一个属性
  • 【大模型】RAG
  • 8.19 note
  • 云原生俱乐部-mysql知识点归纳(1)
  • cesium中实时获取鼠标精确坐标和高度
  • Vue深入组件:组件事件详解1
  • Laravel中如何使用php-casbin
  • OSCP - Proving Grounds - Vanity
  • 云计算核心技术之容器技术
  • SAP 数据脱敏工具:SNP TDO如何满足新颁敏感信息政策要求
  • 【C语言篇】操作符详解
  • 电子电气架构 --- 软件开发数字化转型
  • Python函数:装饰器
  • 三高架构杂谈
  • 软件定义汽车---创新与差异化之路
  • Jenkins全链路教程——Jenkins调用Maven构建项目