ArrayList的原理是数组,所以实现其add,remove,get,set等方法,仅需要考虑的是怎么在数组中实现。
另外隐含的问题是,数组在何时需要扩容,以及下标是否合法。
package com.ykq;
/**
* 自己实现一个ArrayList,帮助理解ArrayList的底层结构
* @author RSA
*
*/
public class MyArrayList {
//ArrayList的原理是数组
private Object[] elementData;
//List元素的个数,也可表示下一个将插入元素的下标
private int size;
//size不能直接对外,所以提供一个查询方法
public int size() {
return size;
}
//判断List是否为空,原理仅需判断size是否为0
public boolean isEmpty() {
return size == 0 ? true : false;
}
//获取List指定下标的值,原理获取数组指定下标的值
public Object get(int index) {
rangeCheck(index);
return elementData[index];
}
//删除List指定位置的对象,原理首先判断下标是否合法,再将index之后的所有元素向前移一位,再将原最后一位的元素置空
public void remove(int index) {
rangeCheck(index);
//需要复制的元素个数
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null;
}
//删掉List某一个对象,原理将域指定对象相同的下标找出,然后调用删除List指定位置的对象的方法
public void remove(Object obj) {
for (int i = 0; i < size; i++) {
if (get(i).equals(obj) ) {
remove(i);
}
}
}
//为已有的下标,重新赋值,原理数组的赋值
public Object set(int i, Object obj) {
rangeCheck(i);
Object oldValue = elementData[i];
elementData[i] = obj;
return oldValue;
}
//范围判断
private void rangeCheck(int index) {
//size是当前已存数据的长度,比某位元素下标多1。所以在比较时减1。为什么不需要判断index<0,原因是size本就是不小于0的,所以不用比
if (index > size-1) {
try {
throw new IllegalArgumentException("下标不合法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//无参构造方法。将数组length设为默认值
public MyArrayList(){
//ArrayList的默认长度是10,为便于测试,遂初始为3
this(3);
}
public MyArrayList(int initialCapacity){
super();
if (initialCapacity < 0) {
try {
//测试代码,没必要向上抛出异常,所以捕获
new IllegalArgumentException("Illegle Capacity: " + initialCapacity);
} catch (Exception e) {
e.printStackTrace();
}
}
this.elementData = new Object[initialCapacity];
}
//在List末尾添加元素,原理先判断是否扩容,再在最后一位元素后赋值
public void add(Object obj) {
ensureCapacity();
//size默认是0,意味着将入参放到当前位置
elementData[size++] = obj;
}
//在List指定下标新查一个元素,原理判断下标是否合法,在判断是否扩容,在将index及以后的元素往后移一位,将index空出,再为下标index对应的空间赋值
public void add(int index, Object obj) {
rangeCheck(index);
ensureCapacity();
System.arraycopy(elementData, index, elementData, index+1, size-index);
elementData[index] = obj;
size++;
}
//扩容,原理新建数组,并将原数组的值赋值,再让elementData指向新数组
private void ensureCapacity() {
//数组扩容,原理如果标识空闲位的下标(或当前数组已存数据的长湖)size,已经等于数组定义的长度,则不够再放下一个元素,即容量不够
if(size == elementData.length) {
Object[] newArray = new Object[size*2+1];
//将原数组的内容拷贝到新数组
System.arraycopy(elementData, 0, newArray, 0, elementData.length);
elementData = newArray;
// 两种拷贝的方法
// for(int i = 0; i< elementData.length; i++){
// newArray[i] = elementData[i];
// }
}
}
public static void main(String[] args) {
MyArrayList list = new MyArrayList();
//下面四句测试扩容
list.add("1");
list.add("2");
list.add("3");
list.add("4");
//测试下标移除
list.remove(0);
//测试对象移除
list.remove("2");
//测试为指定下标,重新赋值
list.set(1, "5");
//测试在指定下标插入元素
list.add(1, "6");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//测试获取指定下标的元素
System.out.println(list.get(4));
//测试list的元素个数
System.out.println(list.size());
//测试list是否为空
System.out.println(list.isEmpty());
}
}