【源码解析】- ArrayList源码解析,绝对详细 (3)

以图中为例,我们需要将index 1、2、3、4 整体往后挪动,就像有人插队一样,插入的位置后面整体后移了一位。index=0的位置是不用动的。这里的写法应该是

System.arraycopy(elementData, 1, elementData, 1 + 1,5 - 1);

3、将index=1的位置重新赋值,原来index=1的位置已经被移到现在的index=2的位置了。

详细的流程可以通过代码的方式观察即可理解这个过程。

移除元素操作 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } // 指定index 删除其索引位置的元素并返回 public E remove(int index) { // 老规矩,检查index的边界 rangeCheck(index); // 记录操作次数 modCount++; // 找到这个待删除的元素,主要用户返回 E oldValue = elementData(index); // 需要移动的数量 int numMoved = size - index - 1; // 如果需要移动 ,如果只从后面删除的话,例如 size=5 index = 4 ,那么numMoved=0 if (numMoved > 0) // 进行数组拷贝移动,填上那个空位置 System.arraycopy(elementData, index+1, elementData, index, numMoved); // 然后把尾巴多出来的那个元素删掉啦 elementData[--size] = null; // clear to let GC do its work // 返回 return oldValue; } // 删除指定的对象 public boolean remove(Object o) { // ArrayList元素允许为空的 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { // 比较元素,然后找到其所在的index 交由fastRemove通过index移除。与remove(index) for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } // 快速删除 和remove(index) 基本一致 ,在数组index的操作是高效,通过index去操作 private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work } // 全部删除了 public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }

这里我们会发现一个问题啊,我们在静态的数组中进行index所在数据的删除时,一般是直接对 arr[index] = 0; 直接对索引位置的元素进行null赋值。但在ArrayList中就不一定是这样了,他一直都是对最后一位元素进行操作elementData[size—] = null; 我们来画个图看一下

【源码解析】- ArrayList源码解析,绝对详细

例如我们要对上图中index=1的位置元素进行remove操作,怎么做呢?

1、找到index 2、3、4、5需要移动的元素。

2、将他们整体往前移动一位。这个时候需要删除的元素已经被覆盖了

3、再将最后一个删除。(真正移除的那个元素其实和前面一位一样哦)

整体下来发现和add(E e,int index)整个流程好像正好相反,哈哈!

修改元素操作 public E set(int index, E element) { // index 检查 rangeCheck(index); // 找到旧元素 E oldValue = elementData(index); // 替换所在位置的元素 elementData[index] = element; return oldValue; }

这个还是比较简单的。可以理解成是一个替换的操作就可以了。

查询操作 // 指定index 返回其所在的元素 public E get(int index) { // 边界检查 rangeCheck(index); // 返回,这个简单,索引快速定位 return elementData(index); } // 从前往后查询,第一次出现的位置index public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; } // 从后往前查询,第一次出现的位置index public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }

查询操作就简单了很多哈。基本上都是基于索引来访问的。

到这里我们已经总结了很多常用的方法,在ArrayList中还有非常多的方法,例如迭代器Iterator、suList操作等等。这里就不过多进行解析了,不过后面会通过专门的篇幅来介绍迭代器Iterator和为什么不能在for遍历集合时对集合进行remove操作,有时还会抛出异常ConcurrentModificationException。

if (modCount != expectedModCount) { throw new ConcurrentModificationException(); }

这里有一个我们非常熟悉的变量modCount。详细的后面在来解析把。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzpdgz.html