add(int, E)方法
public void add(int index, E element) { // 插入数组位置检查 rangeCheckForAdd(index); // 确保容量,如果需要扩容的话则自动扩容 ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); // 将index后面的元素往后移一个位置 elementData[index] = element; // 在想要插入的位置插入元素 size++; // 元素大小加1 } // 针对插入数组的位置,进行越界检查,不通过抛出异常 // 必须在0-最后一个元素中间的位置插入,,否则就报错 // 因为数组是连续的空间,不存在断开的情况 private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } 复制代码
这个也不难 前面我们数组已经实现了
get方法
public E get(int index) { // 先进行越界检查 rangeCheck(index); // 通过检查则返回结果数据,否则就抛出异常。 return elementData(index); } // 越界检查的代码很简单,就是判断想要的索引有没有超过当前数组的最大容量 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } 复制代码
先检查数组越界,然后你就是直接去数组那边拿数据然后返回
set(int index, E element)
// 作用:替换指定索引的元素 public E set(int index, E element) { // 索引越界检查 rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } 复制代码
这个就是替换指定位置的元素
remove(int):通过删除指定位置上的元素
public E remove(int index) { // 索引越界检查 rangeCheck(index); modCount++; // 得到删除位置元素值 E oldValue = elementData(index); // 计算删除元素后,元素右边需要向左移动的元素个数 int numMoved = size - index - 1; if (numMoved > 0) // 进行移动操作,图形过程大致类似于上面的add(int, E) System.arraycopy(elementData, index+1, elementData, index, numMoved); // 元素大小减1,并且将最后一个置为null. // 置为null的原因,就是让gc起作用,所以需要显示置为null elementData[--size] = null; // clear to let GC do its work // 返回删除的元素值 return oldValue; } 复制代码
remove(Object o)
public boolean remove(Object o) { // 如果删除元素为null,则循环找到第一个null,并进行删除 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { // 根据下标删除 fastRemove(index); return true; } } else { // 否则就找到数组中和o相等的元素,返回下标进行删除 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { // 根据下标删除 fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; // 计算删除元素后,元素右边需要向左移动的元素个数 int numMoved = size - index - 1; if (numMoved > 0) // 进行移动操作 System.arraycopy(elementData, index+1, elementData, index, numMoved); // 元素大小减1,并且将最后一个置为null. 原因同上。 elementData[--size] = null; // clear to let GC do its work } 复制代码
总结
ArrayList的优缺点
- ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快
- ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已
- 删除元素时,涉及到元素复制,如果要复制的元素很多,那么就会比较耗费性能
- 插入元素时,涉及到元素复制,如果要复制的元素很多,那么就会比较耗费性能
为什么ArrayList的elementData是用transient修饰的
- 说明:ArrayList实现了Serializable接口,这意味着ArrayList是可以被序列化的,用transient修饰elementData意味着我不希望elementData数组被序列化
- 理解:序列化ArrayList的时候,ArrayList里面的elementData未必是满的,比方说elementData有10的大小,但是我只用了其中的3个,那么是否有必要序列化整个elementData呢?显然没有这个必要,因此ArrayList中重写了writeObject方法。
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } 复制代码
- 优点:这样做既提高了序列化的效率,减少了无意义的序列化;而且还减少了序列化后文件大小。
版本说明
- 这里的源码是JDK8版本,不同版本可能会有所差异,但是基本原理都是一样的。
结尾
ArrayList就这么多了,大家自己可以对着博客,对着源码看,我感激它这个源码不是很难,基于数组的把可能是 因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。