https://www.cnblogs.com/sx-wuyj/p/11177257.html
自己学习 ArrayList 源码的一些心得记录.
继续上一篇, ArrayList 源码解析(一)
addll(Collection<? extends E> c) : 添加目标集合到原有集合中.
- // 参数需要是 Collection 的子类
- public boolean addAll(Collection<? extends E> c) {
- // 将 C 集合通过 toArray()方法转为数组
- Object[] a = c.toArray();
- // 拿到传入数组的长度
- int numNew = a.length;
- // 对底层数组进行扩容
- ensureCapacityInternal(size + numNew); // Increments modCount
- // 复制数组
- System.arraycopy(a, 0, elementData, size, numNew);
- // 将 size 更新为 size+numNew
- size += numNew;
- // 返回 boolean
- return numNew != 0;
- }
- ensureCapacityInternal(size + numNew);
关于这个方法, 在上一篇博客当中有详细解释, 可以去看看.
add(int index, Collection<? extends E> c): 添加目标集合到原有集合指定位置
- public boolean addAll(int index, Collection<? extends E> c) {
- // 检验 index
- rangeCheckForAdd(index);
- // 将 C 集合通过 toArray()方法转为数组
- Object[] a = c.toArray();
- // 拿到传入数组的长度
- int numNew = a.length;
- // 对底层数组进行扩容
- ensureCapacityInternal(size + numNew);
- // 计算需要复制的长度
- int numMoved = size - index;
- // 需要复制的长度如果大于 0, 通过 arraycopy 方法复制
- if (numMoved> 0)
- System.arraycopy(elementData, index, elementData, index + numNew,
- numMoved);
- // 将参数数组复制到底层数组 index 位置.
- System.arraycopy(a, 0, elementData, index, numNew);
- // 修改 size 的值 为 size+numNew
- size += numNew;
- // 返回 boolean
- return numNew != 0;
- }
其实 addAll()方法只要理解扩容机制, 其他的内容很简单. 但是这个方法需要注意的就是根据 index 去插入的时候.
- if (numMoved> 0)
- System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
- System.arraycopy(a, 0, elementData, index, numNew);
看见源码, 可能会想为什么要复制两次呢? 第一个判断的时候, 其实还有另一层含义: 判断插入位置是否在底层数组中间, 如果是则需要将插入的位置之后的元素向后移动, 也就是复制一个新的数组
第二次的复制才是将两个数组合为一个数组, 也就是底层数组.
get(int index): 获取指定位置的元素
- public E get(int index) {
- rangeCheck(index);
- return elementData(index);
- }
get 方法大家都不陌生, 源码也很简单, 或许对 rangeCheck()方法比较陌生.
- private void rangeCheck(int index) {
- if (index>= size)
- throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
- }
仅此而已, 我们传入的 index 最大值不能超过 size, 这个很好理解.
set(int index, E element): 修改指定位置的元素
- public E set(int index, E element) {
- rangeCheck(index);
- E oldValue = elementData(index);
- elementData[index] = element;
- return oldValue;
- }
set 方法的原理其实就是将底层数组 indxe 的元素修改传入的元素, 至于传入的参数为什么用泛型也很好理解, 我们可以想一下我们工作学习中集合中元素的类型, 各种各样, 所以在这个地方使用泛型的好处不言而喻.
还有一点就是 set 方法是有返回值的, 我之前是没有注意到的, 返回值就是被修改的那个元素.
size(): 集合的大小
- public int size() {
- return size;
- }
indexOf(object o): 集合中某个元素第一次出现的位置
- // 集合中某个元素第一次出现的位置
- public int indexOf(Object o) {
- // 判断传入 o 是否为 null
- if (o == null) {
- // 如果为 null, 循环遍历寻找为 null 的索引位置
- for (int i = 0; i <size; i++)
- // 因为传入的是一个对象, 为 null 使用 ==
- if (elementData[i]==null)
- // 返回出现的位置
- return i;
- } else {
- // 如果不为 null, 使用 equals 寻找传输对象位置
- for (int i = 0; i < size; i++)
- if (o.equals(elementData[i]))
- // 找到返回对象位置
- return i;
- }
- // 找不到对象情况下返回 - 1
- return -1;
- }
这个方法需要注意就是, 如果传入 null 的时候是需要用 == 去寻找的.
lastIndexOf(Object o): 返回元素最后一次出现的位置
- public int lastIndexOf(Object o) {
- // 判断传入 o 是否为 null
- if (o == null) {
- // 如果为 null, 循环遍历寻找为 null 的索引位置
- // 这里是倒序遍历
- for (int i = size-1; i>= 0; i--)
- if (elementData[i]==null)
- return i;
- } else {
- // 如果不为 null, 使用 equals 寻找传输对象位置
- // 同样是倒序遍历
- for (int i = size-1; i>= 0; i--)
- if (o.equals(elementData[i]))
- return i;
- }
- return -1;
- }
其实 indeOf()和 lastIndexOf()两个方法的思路是一样的, 区别就在于一个为正序遍历, 一个倒序遍历.
clone(): 克隆
- public Object clone() {
- try {
- // 调用了父类也就是 Object 的 clone.
- ArrayList<?> v = (ArrayList<?>) super.clone();
- // 集合 V 的底层数组复制一个原集合底层数组
- v.elementData = Arrays.copyOf(elementData, size);
- v.modCount = 0;
- // 返回 V 集合
- return v;
- } catch (CloneNotSupportedException e) {
- // this shouldn't happen, since we are Cloneable
- throw new InternalError(e);
- }
- }
这个方法我个人认为是不是返回值可以为 ArrayList, 从源码可以看出来返回去 v 是 ArrayList. 但是 ArrayList 的顶级父类也是 Object. 所以这个也不算错.
try-catch 中的注释意思是 这是不可能发生的, 因为我们是克隆的
toArray(): 将集合装换为 Object 类型数组
- public Object[] toArray() {
- return Arrays.copyOf(elementData, size);
- }
其实就是将底层数组复制一个返回去, 底层调用的依然还是调用了 System.arraycopy()这个方法, 这里不展开说.
toArray(T[] a): 将集合装换为指定类型数组
- public <T> T[] toArray(T[] a) {
- // 如果参数数组的长度小于 size, 那么直接转换为参数数组类型
- if (a.length <size)
- // Make a new array of a's runtime type, but my contents:
- return (T[]) Arrays.copyOf(elementData, size, a.getClass());
- System.arraycopy(elementData, 0, a, 0, size);
- // 如果参数数组长度大于 size
- // 那么返回去的数组 [size] 为 null
- if (a.length> size)
- a[size] = null;
- return a;
- }
这两个重载方法的区别就是, 第一个会返回一个 Object 类型的数组, 第二个会返回一个指定类型的数组.
remove(int index): 删除指定位置的元素
- public E remove(int index) {
- // 校验
- rangeCheck(index);
- modCount++;
- // 被删除的元素
- E oldValue = elementData(index);
- // 需要被复制的元素个数
- int numMoved = size - index - 1;
- // 如果被复制的元素个数大于 0, 那么就复制一个数组
- if (numMoved> 0)
- System.arraycopy(elementData, index+1, elementData, index,
- numMoved);
- // 将底层数组最后一个元素设置为 null
- // 让 GC 开始工作
- elementData[--size] = null;
- // 返回被删除的元素
- return oldValue;
- }
如果被复制的元素葛素小于或者等于 0, 那么意味着删除的是末尾的元素, 不需要变更元素位置.
remove(Object o): 删除指定元素
- public boolean remove(Object o) {
- // 判断需要删除的对象是否为 null
- if (o == null) {
- // 遍历底层数组, 使用 ==null 来寻找
- for (int index = 0; index <size; index++)
- if (elementData[index] == null) {
- fastRemove(index);
- return true;
- }
- } else {
- // 如果不是 null, 是一个对象
- // 遍历底层数组, 使用 equals 来寻找
- for (int index = 0; index < size; index++)
- if (o.equals(elementData[index])) {
- fastRemove(index);
- return true;
- }
- }
- // 如果没有找到返回 false
- return false;
- }
同样是重载, 其中 fastRemove()方法和 remove()逻辑基本一致, 只是没有返回值. 并且被 private 修饰, 是一个私有方法.
- private void fastRemove(int index) {
- modCount++;
- // // 需要被复制的元素个数
- int numMoved = size - index - 1;
- // 如果被复制的元素个数大于 0, 那么就复制一个数组
- if (numMoved> 0)
- System.arraycopy(elementData, index+1, elementData, index,
- numMoved);
- // 将底层数组最后一个元素设置为 null
- // 让 GC 开始工作
- elementData[--size] = null; // clear to let GC do its work
- }
clean(): 清楚集合内部元素, 成为一个空集合
- public void clear() {
- modCount++;
- // clear to let GC do its work
- for (int i = 0; i < size; i++)
- elementData[i] = null;
- size = 0;
- }
这个应该一目了然了吧, 就是循环遍历底层数组, 将底层数组所有的元素设置为 null, 并将 size 修改为 0;
个人的一些简单理解. 还有一部分方法没有写. 时间和篇幅有限, 先写到这里, 之后再继续更新.
如果在阅读的过程中由. 于本人水平有限带来的误导, 希望大佬可以批评指正. 共同进步
来源: https://www.cnblogs.com/sx-wuyj/p/11733775.html