动态数组的实现 - java
1、说明
动态数组在 JDK中有其源代码,类的名字叫做 ArrayList,本文是对于其部分方法的复现,部分方法的实现略有不同,通过下面代码的学习,可以对于 JDK 中的动态数组有更佳直观的认识,对于源代码的阅读也有一定的好处
2、作者自己实现的动态数组方法如下所示:
3、作者自己实现的原始代码
/**
* 对于范型的操作
* 1、在写类的时候,表明这是一个范型()范型类
*/
/**
* 对象数组
* Object[] object = new Object[7];
* object[0] = new Person(10,"Hello");
* 对象数组中保存的元素是对象的引用地址,不是对象本身;
* 上面的 object[0] 存储的是对象的地址,不是对象本身;
* <p>
* 为什么放地址?
* 1、节省空间
* 2、假设对象占有多个字节,很难在索引地方塞下
*/
public class myArrayList<E> {
public static final int ELEMENT_NOT_FOUND = -1;
// static 静态的 在代码区中只有一份 变量
// 定义无参构造的对象的默认大小
private static final int DEFAULT_CAPACITY = 10;
/*
定义了两个成员变量
数组的大小
数组的存储元素 E<> 使用了泛型,进行元素的存储
*/
private E[] elements; // 开辟的内存空间是 E 数据类型的连续存储
private int size; // 下一个要放置的位置索引 [0,size-1]是已经放置好的元素
//有参构造
public myArrayList(int capacity) {
// 使用三目运算符,判断,使用的容量的大小
capacity = (capacity <= DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
// 进行强制的类型转换
// new 创建的数组大小,使用 Object 可以存放多种数据类型
// 创建数组的语法:new int[10];
// 在最后必须进行强制的类型转换即可
elements = (E[]) new Object[capacity];//新建容量大小
size = 0;//数组里元素个数
}
//无参构造 默认的空间大小为 10
public myArrayList() {
// 通过无参数的构造函数,调用有参数的构造函数,使用 this
this(DEFAULT_CAPACITY);
}
//判断是否为空
public boolean isEmpty() {
return size == 0;
}
//返回元素个数
public int getSize() {
return size;
}
// 向数组中添加元素
// 添加到数组的最后面
public void add(E e) {
// 此处调用了加入的方法,根据位置,进行元素的加入
add(size, e);
}
//向头部添加元素
public void addFirst(E e) {
add(0, e);
}
// 在数组中的任意位置,进行元素的添加
// 从数组的最后面移动,空出来 index
// 删除是小的先向前面移动
public void add(int index, E e) {
// 对于空数据的处理
// if (e == null) return;
// 判断当前的索引是否是正确的,正确之后,进行元素的添加
// 此处的插入中, index > size 可以等于 size 因为相当于在数组的最后面插入,是可以插入进去的,前提是小于容器的最大索引
rangeCheckForAdd(index);
// if (index < 0 || index > size) {
// throw new IndexOutOfBoundsException("超出了索引");
// }
// 进行扩容操作
// size 的容量就是当前的数组可以存储的最多元素的数量,比它大的时候,当这个数量满的时候,进行扩容即可
// size 真正存在的元素的个数
if (size == elements.length) {
resize(2 * elements.length);
}
// 添加的位置,给空出来,然后加进去
// 循环的条件是:从 index 一直到 size - 1 移动即可
for (int i = size - 1; i >= index; i--) {
elements[i + 1] = elements[i];
}
elements[index] = e;
size++;
}
//扩容操作
private void resize(int i) {
// 申请更大的数组,将数组拷贝过去
// new int[] new Object[] 之间是有区别的,创建对象数组,创建普通数组
E[] newarr = (E[]) new Object[i];
for (int j = 0; j < size; j++) {
newarr[j] = elements[j];
}
// 引用地址改变给原来的名字
// newarr 垃圾回收器会自动回收
elements = newarr;
}
//打印数组
@Override
public String toString() {
// java 中进行字符串的拼接 使用 StringBuilder,提升效率
StringBuilder res = new StringBuilder();
res.append("size=" + size + "\nacpiticy=" + elements.length + "\n\n");
res.append("[");
for (int i = 0; i < size; i++) {
res.append(elements[i]);
// size 是数组的大小,实际比索引是大一个的,所以此处需要减去 1
if (i != size - 1) {
res.append(",");
}
}
res.append("]");
return res.toString();
}
//取出元素
public E get(int index) {
// index 进行约束
rangeCheck(index);
// if (index < 0 || index >= size) {
// // 拼接字符串,抛出异常
// throw new IndexOutOfBoundsException("Index:" + index + ",Size:" + size);
// }
return elements[index];
}
// 给指定位置赋值
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
}
// 找到索引,进行赋值
elements[index] = e;
}
// 数组中是否包含制定元素
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (elements[i].equals(e)) {
return true;
}
}
return false;
}
// 获取数组中指定元素的索引
// 在泛型中,传进来的对象数据类型,与实际的泛型中保存的泛型进行对比
public int find(E e) {
for (int i = 0; i < size; i++) {
if (elements[i].equals(e)) {
return i;
}
}
return ELEMENT_NOT_FOUND;
}
// 删除第一个数
public E removeFirst() {
return remove(0);
}
// 删除最后一个数
public E removeLast() {
return remove(size - 1);
}
// 删除指定索引数
public E remove(int index) {
// 对于索引的检查
rangeCheck(index);
E res = elements[index];
// 索引后面的元素,集体移动
for (int i = index + 1; i < size; i++) {
// 进行向前移动
elements[i - 1] = elements[i];
}
// 当最后一个元素,移动到前面的时候,最后一个元素不用处理,因为 size 已经进行了移动,后面的元素是无法进行访问的
size--;
// 对象数组的最后面,进行对象的销毁
elements[size] = null;
// 对于代码进行扩容的操作
if (size == elements.length / 4) {
resize(elements.length / 2);
}
return res;
}
// 删除指定元素
public void removeElement(E e) {
int index = find(e);
if (index != -1) {
remove(index);
}
}
// 查找元素,找到之后,返回其索引
public int indexOf(E element) {
// 允许了空值的传递,此处调用 下面的 equals() 会报错,需要处理
if (element == null) {
// 找到的数组中第一个空元素
for (int i = 0; i < size; i++) {
if (elements[i] == null) return i;
}
} else {
for (int i = 0; i < size; i++) {
// 判断当前的对象与传入进去的对象是否一致
// == 只是会比较引用地址
// 把 element 放到前面,因为已经判断了,过来一定是非空的,使用 equals() 比较即可
if ((element.equals(elements[i]))) {
return i;
}
}
}
return ELEMENT_NOT_FOUND;
}
/**
* clear
* 直接使得 size = 0 ,表面上实现了数组的清空,可以节约a运行时间
* <p>
* 清理对象数组的顺序,引用数据,指向堆中的对象,之间的连接线断开之后,对象将会被垃圾回收
*/
public void clear() {
// 对于对象数组,单单使用 size = 0,会导致对象无法被完全的回收,造成内存的浪费,需要使得所有的对象的引用地址都为 null
// 将内存地址清空
// element == null 直接将数组干掉,没有必要,需要进行重新创建,消耗时间
// 让数组里面指向的对象干掉
// 可以循环利用的留下来,减少时间的消耗
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
}
// 验证添加元素的索引
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
}
}
// 验证其他的非添加时候的索引,索引不能等于最大个数,因为会超过容器大小
// private 只有在同一个类中是可以调用的
// public 任何类中都可以调用
private void rangeCheck(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index:" + index + ",Size" + size);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!