目录
1、核心原理:数组的动态扩容,当一个数组容量不够的时候,创建一个新的数组,将旧的数组的内容复制到新的数组上。
3.2 add方法:有两种重载形式:一种是在末尾追加,另一种在任意位置添加(区间在size范围内)同时在添加的时候应做两个检查,首先要检查传入的索引是否合法,另一个保证容量足够(不够将进行扩容)
3.3元素的更新操作,在此提供了两种更新的操作,一种是根据元素元素来操作,一种是根据索引来操作
3.4元素的删除操作,两种方式,一种是根据索引来删除(要检查索引的合法性),一种是根据元素来删除。
3.6ArrayList判空,思路为:判定其size是否为0,代码如下
3.7清空元素,思路,仅需把有所有的元素置为null和size=0即可。代码块如下:
1、核心原理:数组的动态扩容,当一个数组容量不够的时候,创建一个新的数组,将旧的数组的内容复制到新的数组上。
2、主要实现的功能:元素的增加,删除,修改,查看、判空
3、具体细节:
3.0 定义的一些常量代码段如下:
/**
* description 元素的个数
*/
private int size;
/**
* description 泛型类数组
*/
private E[] elements;
/**
* description 定义初始化默认容量为10
*/
private static final int DEFAULT_INITIAL_CAPACITY = 10;
/**
* description 元素未找到抛出-1
*/
private static final int ELEMENT_NOT_FOUND = -1;
/**
* description 元素不存在
*/
private final E ELEMENT_NOT_DOES_EXIST = null;
3.1构造器,两个,一个有参构造,一个无参构造代码段如下:
/**
* description 有参构造器,传入的参数小于小于默认值时采用默认值,否则采用传入的值
*
* @param capacity 传入的容量值
*/
public MyArrayList(int capacity) {
if (capacity >= DEFAULT_INITIAL_CAPACITY) {
elements = (E[]) new Object[capacity];
} else {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
}
/**
* description 无参构造器,采用默认值
*/
public MyArrayList() {
this(DEFAULT_INITIAL_CAPACITY);
}
3.2 add方法:有两种重载形式:一种是在末尾追加,另一种在任意位置添加(区间在size范围内)同时在添加的时候应做两个检查,首先要检查传入的索引是否合法,另一个保证容量足够(不够将进行扩容)
检查索引是否合法的方法:
/**
* description 检查是否越界
*
* @param index 待检测的索引
*/
private void isOutOfBoundsForAdd(int index) throws IllegalAccessException {
if (index <= size && index >= 0) return;
throw new IllegalAccessException("传入的索引越界");
}
保证容量足够的方法:扩容采用位运算,加快了运算速度,不知道位运算为何物的可参考我的另一篇博客
/**
* description 保证容量足够
*/
private void ensureCapacity() {
if (size < elements.length) return;
/*
* elements.length >> 1,位运算,相当于除以二,加快运算速度
* 等价于 int C = elements.length *1.5;
*/
int C = elements.length + (elements.length >> 1);
E[] newElements = (E[]) new Object[C];
//数组复制
for (int i = 0; i < elements.length; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println("已扩容,新容量为:" + elements.length);
}
在任意位置添加的方法
/**
* description 从指定地方添加元素
*
* @param index 指定的索引
* @param element 传入的元素
* @throws IllegalAccessException 传入的索引越界异常
*/
public void add(int index, E element) throws IllegalAccessException {
isOutOfBoundsForAdd(index);
ensureCapacity();
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
在末尾追加的时候只需要调用add(int index, E element)方法即可
代码块如下:
/**
* description 添加元素的方法,默认从末尾追加
*
* @param element 传入一个添加的元素
*/
public void add(E element) throws IllegalAccessException {
add(size, element);
}
3.3元素的更新操作,在此提供了两种更新的操作,一种是根据元素元素来操作,一种是根据索引来操作
思路,不管是根据元素还是索引,最后都是将对应索引元素进行替换,因此,我们先给出根据索引更新元素的值的方法,同时他应该告诉外界我们更新的元素,这样别人在调用我们的接口的时候会更加方便,注意判定索引是否合法
/**
* description 更新指定索引的元素
*
* @param index 需要更新元素的索引的值
* @param element 传入的更新元素
* @return 更新前的元素
*/
public E update(int index, E element) {
if(index>size||index<0) return ELEMENT_NOT_DOES_EXIST;
E e = elements[index];
elements[index] = element;
return e;
}
根据元素来更新元素的方法,上文分析到,我们最终是要通过索引来操作,因此,我们可以在此封装一个给一个指定元素返回其索引的方法,代码段如下:
/**
* description 根据元素返回其索引,未找到返回-1
*
* @param element 需要查找的元素的索引
* @return 返回索引
*/
public int returnIndex(E element) {
for (int i = 0; i < elements.length; i++) {
if (elements[i].equals(element)) return i;
}
return ELEMENT_NOT_FOUND;
}
然后就可以通过元素来更新元素啦(返回对应元素的索引)!代码块如下:
/**
* description 将一个元素替换为另一个元素,并返回被替换元素的索引
*
* @param e1 欲替换的元素
* @param e2 替换后的元素
* @return
*/
public int update(E e1, E e2) {
int index = returnIndex(e1);
if (index < 0) return index;
update(index, e2);
return index;
}
3.4元素的删除操作,两种方式,一种是根据索引来删除(要检查索引的合法性),一种是根据元素来删除。
根据索引删除(返回对应的元素)
/**
* description 根据索引删除元素
* @param index 需要删除的元素索引
* @return 返回被删除的元素
*/
public E remove(int index) {
if (index < 0) return ELEMENT_NOT_DOES_EXIST;
E element = elements[index];
for (int i = index; i < size; i++) {
elements[i] = elements[i + 1];
}
size--;
return element;
}
根据元素删除,在这删除之前调用returnElement(E element)方法
然后再调用根据索引删除的方法
/**
* description 按照元素删除指定的元素
*
* @param element 传入要删除的元素
*/
public void remove(E element) {
int index = returnIndex(element);
remove(index);
}
3.5根据索引查看元素
/**
* description 根据元素返回其索引,未找到返回-1
*
* @param element 需要查找的元素的索引
* @return 返回索引
*/
public int returnIndex(E element) {
for (int i = 0; i < elements.length; i++) {
if (elements[i].equals(element)) return i;
}
return ELEMENT_NOT_FOUND;
}
3.6ArrayList判空,思路为:判定其size是否为0,代码如下
/**
* description 判断是否有元素
* @return 布尔值
*/
public boolean isEmpty(){
return size==0;
}
3.7清空元素,思路,仅需把有所有的元素置为null和size=0即可。代码块如下:
/**
* description 元素清空
*/
public void clear(){
for (int i = 0; i < size; i++) {
elements[i]=null;
}
size=0;
}
3.8 toString()方法
@Override
public String toString() {
return "MyArrayList{" +
"size=" + size +
", elements=" + Arrays.toString(elements) +
'}';
}
4、测试用例
/**
* @author Mr.xiong
* @version v1.0
* @description ArrayList测试类
* @time 2022/3/26 12:05
* @since jdk8
*/
public class MyArrayListTest {
public static void main(String[] args) throws IllegalAccessException {
MyArrayList<Integer> myArrayList = new MyArrayList<>();
for (int i = 0; i < 15; i++) {
myArrayList.add(i+1);
}
myArrayList.add(0,14);
System.out.println(myArrayList.update(2, (Integer)222));
System.out.println(myArrayList.update((Integer)13,(Integer)444 ));
System.out.println(myArrayList);
System.out.println(myArrayList.returnIndex(15));
myArrayList.remove((Integer)444);
System.out.println(myArrayList.remove(2));
System.out.println(myArrayList);
myArrayList.clear();
System.out.println(myArrayList.isEmpty());
System.out.println(myArrayList);
}
}
执行的结果如下:
5、完整源代码
package mylist;
import java.util.Arrays;
/**
* @author Mr.xiong
* @version v1.0
* @description 自定义实现arraylist
* @time 2022/3/26 11:10
* @since jdk8
*/
@SuppressWarnings("unchecked")
public class MyArrayList<E> {
/**
* description 元素的个数
*/
private int size;
/**
* description 泛型类数组
*/
private E[] elements;
/**
* description 定义初始化默认容量为10
*/
private static final int DEFAULT_INITIAL_CAPACITY = 10;
/**
* description 元素未找到抛出-1
*/
private static final int ELEMENT_NOT_FOUND = -1;
/**
* description 元素不存在
*/
private final E ELEMENT_NOT_DOES_EXIST = null;
/**
* description 有参构造器,传入的参数小于小于默认值时采用默认值,否则采用传入的值
*
* @param capacity 传入的容量值
*/
public MyArrayList(int capacity) {
if (capacity >= DEFAULT_INITIAL_CAPACITY) {
elements = (E[]) new Object[capacity];
} else {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
}
/**
* description 无参构造器,采用默认值
*/
public MyArrayList() {
this(DEFAULT_INITIAL_CAPACITY);
}
/**
* description 添加元素的方法,默认从末尾追加
*
* @param element 传入一个添加的元素
*/
public void add(E element) throws IllegalAccessException {
add(size, element);
}
/**
* description 从指定地方添加元素
*
* @param index 指定的索引
* @param element 传入的元素
* @throws IllegalAccessException 传入的索引越界异常
*/
public void add(int index, E element) throws IllegalAccessException {
isOutOfBoundsForAdd(index);
ensureCapacity();
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
/**
* description 保证容量足够
*/
private void ensureCapacity() {
if (size < elements.length) return;
/*
* elements.length >> 1,位运算,相当于除以二,加快运算速度
* 等价于 int C = elements.length *1.5;
*/
int C = elements.length + (elements.length >> 1);
E[] newElements = (E[]) new Object[C];
//数组复制
for (int i = 0; i < elements.length; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println("已扩容,新容量为:" + elements.length);
}
/**
* description 检查是否越界
*
* @param index 待检测的索引
*/
private void isOutOfBoundsForAdd(int index) throws IllegalAccessException {
if (index <= size && index >= 0) return;
throw new IllegalAccessException("传入的索引越界");
}
/**
* description 更新指定索引的元素
*
* @param index 需要更新元素的索引的值
* @param element 传入的更新元素
* @return 更新前的元素
*/
public E update(int index, E element) {
if(index>size||index<0) return ELEMENT_NOT_DOES_EXIST;
E e = elements[index];
elements[index] = element;
return e;
}
/**
* description 将一个元素替换为另一个元素,并返回被替换元素的索引
*
* @param e1 欲替换的元素
* @param e2 替换后的元素
* @return
*/
public int update(E e1, E e2) {
int index = returnIndex(e1);
if (index < 0) return index;
update(index, e2);
return index;
}
/**
* description 根据元素返回其索引,未找到返回-1
*
* @param element 需要查找的元素的索引
* @return 返回索引
*/
public int returnIndex(E element) {
for (int i = 0; i < elements.length; i++) {
if (elements[i].equals(element)) return i;
}
return ELEMENT_NOT_FOUND;
}
/**
* description 按照元素删除指定的元素
*
* @param element 传入要删除的元素
*/
public void remove(E element) {
int index = returnIndex(element);
remove(index);
}
/**
* description 根据索引删除元素
* @param index 需要删除的元素索引
* @return 返回被删除的元素
*/
public E remove(int index) {
if (index < 0) return ELEMENT_NOT_DOES_EXIST;
E element = elements[index];
for (int i = index; i < size; i++) {
elements[i] = elements[i + 1];
}
size--;
return element;
}
/**
* description 判断是否有元素
* @return 布尔值
*/
public boolean isEmpty(){
return size==0;
}
/**
* description 元素清空
*/
public void clear(){
for (int i = 0; i < size; i++) {
elements[i]=null;
}
size=0;
}
@Override
public String toString() {
return "MyArrayList{" +
"size=" + size +
", elements=" + Arrays.toString(elements) +
'}';
}
}
6、时间复杂度分析
由于数组在内存上地址是连续的
查询复杂度分析:查询的效率是O(1)级别,所以数组查询的效率特别高
添加的复杂度分析:最好O(1),最坏O(2n),平均O(n)
删除复杂度分析:最好O(1),最坏O(n),平均O(n)
更新复杂度:最好O(1)所以数组更新的效率特别高