List的实现类有两个: ArrayList(底层数组实现的)和LinkedList(底层是链表实现的)
ArrayList:由于它的底层实现是数组,所以它的大多数方法与数组下标的操作相关。可能对JDK源码感兴趣的同学有这方面的体会,今天我们就以自己的方式实现一个简单的ArrayList(需要注意的是,这里并不会实现所有的方法,而且在方法的优劣性上肯定是不足以媲美JDK源码的),这里主要是让大家对ArrayList的理解不单单停留在增,删,改的层面上
先看一下JDK源码中ArrayList的方法(我们可以看到它的方法非常的多,但在实际应用中并不是全部都需要掌握,我们只需要理解几个重要的方法,那么用起ArrayList就相当得心应手了)
先定义两个变量
private Object[] elementData;
private int size;(由于ArrayList的底层实现是数组应该先定义一个Object类型的数组对象)
第一个方法:检查下标是否合法
代码的实现思路:
是传入某个具体的下标,判断它是否合法(若为负数或者是超过容器的长度都是不合法)。虽然它的实现相当简单,不过由于很多方法都是基于下标操作的,所以它也显得十分重要。
private void rangeCheck(int index) {
if(index<0||index>=size) {
try {
System.out.println("越界了");
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
}
第二个方法:给容器添加对象(末尾)
代码实现思路:添加某个元素时:
1.我们应该首先判断容器的大小,如果没有超过预先设置好的数组长度,那我们直接在数组的下一位增加一个值即可.
2.如果添加时,发现它的长度已经超过预先数组设置的长度时,我们就应该先对数组进行扩容。
1)扩容时,由于定义时数组的长度是定长。这时我们只能通过重新建立一个新数组,然后再把老数组复制到新数组上, 这就实现 了数组的扩容了。
2)在这里我们运用了System为我们封装好的数组复制方法**:public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)**
src:源数组
srcPos:从源数组的起始位置开始
dest:目标数组
destPos:目标数组的开始位置
length:要copy数组的长度
public void add(Object obj) {
//数组扩容和数据的拷贝
if(size==elementData.length) {
Object[] newArray=new Object[size*2+2];
System.arraycopy(elementData, 0, newArray, 0, elementData.length);
elementData=newArray;
}
elementData[size++]=obj;
}
第三个方法:给容器指定的为添加对象
代码的实现思路:给容器指定的位置添加对象时,
1)我们应该先进行插入的下标位置检查,如果添加操作时的长度没有超过预先设置好的数组长度,那么直接在指定位置添加即可,
2)如果超过指定长度,那我们应该进行数组扩容后,再把元素添加到指定的位置
public void add(int index,Object element) {
if(index>size||index<0) {
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
//数组扩容和数据的拷贝
if(size==elementData.length) {
Object[] newArray=new Object[size*2+2];
System.arraycopy(elementData, 0, newArray, 0, elementData.length);
elementData=newArray;
}
System.arraycopy(elementData, index, elementData, index+1, size-index);
elementData[index]=element;
size++;
}
上面的代码中比较有疑惑的应该还是添加新元素时进行的数组拷贝:System.arraycopy(elementData, index, elementData, index+1, size-index),各参数值的确定。
elementData:表示原来的数组
index:表示从index的位置开始的数据都要往后挪(因为它插入时时的下标为index)
elementData:表示原来的数组
index+1:表示挪动时新开始的位置
size-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;
}
第五个方法:移除指定对象
代码实现思路: 遍历整个数组,用equal()方法匹配到相同的元素就可以调用上面封装的的remove()方法
public void remove(Object obj) {
for(int i=0;i<size;i++) {
if(get(i).equals(obj)) { //注意底层调用的是equal方法
remove(i);
}
}
}
上面是比较常用的五个方法的实现,如果有更好的想法可以直接在评论区留言。
下一节我们将会讲解LinkedList