工作两年还没看过ArrayList中remove、removeAll、clear方法源码的都来报道吧

1,547 阅读6分钟

前言

点赞在看,养成习惯。

点赞收藏,人生辉煌。

点击关注【微信搜索公众号:编程背锅侠】,防止迷路。

删除方法表格

方法名 描述
public E remove(int index) 根据索引删除元素
public boolean remove(Object o) 根据元素删除元素
public void clear() 将集合清空
public boolean removeAll(Collection<?> c) 删除与给定集合中相同的元素

public E remove(int index) 根据索引删除元素

案例演示

@Test
public void test_remove_index(){
 ArrayList<String> list = new ArrayList<>();
 list.add("洛洛01");
 list.add("洛洛02");
 list.add("洛洛03");
 list.forEach(System.out::println);
 // 索引删除
 list.remove(1);
 list.forEach(System.out::println);
}

源码分析

public E remove(int index) {
  // 校验这个索引是否在集合中存在
 rangeCheck(index);
  // 记录修改的次数
 modCount++;
  // 将index对应的元素赋值给 oldValue
 E oldValue = elementData(index);

  // 计算集合中需要移动元素个数
 int numMoved = size - index - 1;
  // 判断要移动的元素个数是否大于0
 if (numMoved > 0)
    // 能进到这里面要删除的元素肯定不在集合的最后面
    // 如果需要移动元素个数大于0,就使用arrayCopy方法进行拷贝 
    // 注意:数据源和目标数据都是elementData
  System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
  // 将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收
 elementData[--size] = null; // clear to let GC do its work
  // 返回被删除的元素
 return oldValue;
}

elementData数组中元素的变化

  • 源数组中的元素

  • System.arraycopy执行前数组中元素

  • System.arraycopy执行后数组中元素-1
  • System.arraycopy执行后数组中元素-2

总结

根据索引删除元素,返回被删除的元素。重点关注elementData数组中元素的变化,可以帮助理解。

System.arraycopy方法不明白的参考这篇文章

public boolean remove(Object o) 根据元素删除元素

案例演示

@Test
public void test_remove_v(){
 ArrayList<String> list = new ArrayList<>();
 list.add("洛洛01");
 list.add("洛洛02");
 list.add("洛洛03");
  list.add("洛洛04");
 list.forEach(System.out::println);
 // 值删除
 list.remove("洛洛03");
 list.forEach(System.out::println);
}

源码分析

public boolean remove(Object o) {
  // 判断要删除的元素是否为null
 if (o == null) {
    // 被删除的元素为null,遍历这个集合
  for (int index = 0; index < size; index++)
      // 判断集合的元素是否为null
   if (elementData[index] == null) {
        // 如果相等,调用fastRemove方法快速删除
    fastRemove(index);
    return true;
   }
 } else {
    // 被删除的元素不为空,遍历集合
  for (int index = 0; index < size; index++)
      // 用o对象的equals方法和集合每一个元素进行比较
   if (o.equals(elementData[index])) {
        // 如果相等,调用fastRemove方法快速删除
    fastRemove(index);
    return true;
   }
 }
  // 如果集合没有o该元素,那么就会返回false
 return false;
}

// 根据索引快速删除方法
private void fastRemove(int index) {
  // 记录修改的次数
 modCount++;
  // 计算要移动元素的个数
 int numMoved = size - index - 1;
  // 如果需要移动的个数大于0,调用arrayCopy方法进行拷贝,判断是不是在尾部插入,大于0不是在尾部
 if (numMoved > 0)
  System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
  // 将集合最后一个元素置为null,尽早被释放
 elementData[--size] = null; // clear to let GC do its work
}

elementData数组中元素的变化

  • 源数组中的元素

  • System.arraycopy执行前数组中元素

  • System.arraycopy执行后数组中元素

总结

根据给定的元素删除集合中与之匹配的元素。返回值为是否删除成功的布尔值。

System.arraycopy方法不明白的参考这篇文章

public void clear()将集合清空

案例演示

@Test
public void test_clear(){
 ArrayList<String> list = new ArrayList<>();
 list.add("洛洛01");
 list.add("洛洛02");
 list.forEach(System.out::println);
 list.clear();
 list.forEach(System.out::println);
}

源码分析

public void clear() {
  // 实际修改集合次数++
 modCount++;

 // 遍历集合,将集合每一个索引对应位置上的元素都置为null,尽早让其释放
 for (int i = 0; i < size; i++)
  elementData[i] = null;
  // 集合长度更改为0
 size = 0;
}

elementData数组中元素的变化

  • 源数组中的元素

  • 清空以后的数组

总结

将集合清空。这个方法会将集合每一个索引对应位置上的元素都置为null,为的是尽早让垃圾收集器回收。

public boolean removeAll(Collection<?> c)删除与给定集合中相同的元素

案例演示

@Test
public void test_remove_all(){
 ArrayList<String> list = new ArrayList<>();
 list.add("洛洛01");
 list.add("洛洛02");
 list.forEach(System.out::println);
 ArrayList<String> all = new ArrayList<>();
 all.add("洛洛01");
 all.add("洛洛05");
 list.removeAll(all);
 list.forEach(System.out::println);
}

源码分析

public boolean removeAll(Collection<?> c) {
  // 校验集合是否为空,为空抛出空指针异常
 Objects.requireNonNull(c);
  // 批量删除
 return batchRemove(c, false);
}

elementData数组中元素的变化

  • 源数组中的元素

  • 源数组变化以后的元素

总结

删除与给定集合中相同的元素。这个方法的主要实现是batchRemove方法。

private boolean batchRemove(Collection<?> c, boolean complement)批量删除方法

private boolean batchRemove(Collection<?> c, boolean complement) {
  // 将原始数组的地址赋值给elementData
 final Object[] elementData = this.elementData;
  // r:用于遍历原始数组,原始数组中元素的索引, w:记录的是未被删除元素的个数
 int r = 0, w = 0;
  // modified:是否删除成功给个默认值false
 boolean modified = false;
 try {
    // 遍历原始数组,size为原始数组的长度
  for (; r < size; r++)
      // complement的给定的值为false,判断指定的集合c是否不包含这个元素
   if (c.contains(elementData[r]) == complement)
        // 指定的集合c中不包含原始数组中的元素,将这个元素放到elementData数组中。
        // 这个循环执行完毕,elementData数组中存放的就是从索引0开始存放未被删除的元素,和后面可能有要被删除的和未被删除的元素,总的长度是原始数组的size。被删除的元素会留在原位置,未被删除的元素原位置有一份,还有一份复制到前面。
    elementData[w++] = elementData[r];
 } finally {
  // 正常情况下r == size的,这个不等于是抛出了异常
  if (r != size) {
      // 数组的拷贝,参看我的其他文章,有这个方法的源码详解
   System.arraycopy(elementData, r,
     elementData, w,
     size - r);
      // 计算修改的次数
   w += size - r;
  }
    // 判断w【记录未被删除的元素的个数】是否等于元素数组的长度
  if (w != size) {
   // clear to let GC do its work,方便垃圾回收,将elementData数组从索引i=w开始,置空每个元素
   for (int i = w; i < size; i++)
        // 置空元素
    elementData[i] = null;
      // size - w删除元素的个数,modCount记录的是修改的次数,每删除一个元素modCount加1
   modCount += size - w;
      // 删除执行完以后集合的长度
   size = w;
      // 删除成功modified 赋值为true
   modified = true;
  }
 }
  // 返回是否删除成功
 return modified;
}

ArrayList系列文章

第一篇:ArrayList中的构造方法源码在面试中被问到了...抱歉没准备好!!!告辞
第二篇:面试官让我讲ArrayList中add、addAll方法的源码...我下次再来
第三篇:工作两年还没看过ArrayList中remove、removeAll、clear方法源码的都来报道吧

创作不易, 非常欢迎大家的点赞、评论和关注(^_−)☆

你的点赞、评论以及关注是对我最大的支持和鼓励,而你的支持和鼓励

我继续创作高质量博客的动力 !!!

掘金征文 | 2020 与我的年中总结 征文活动正在进行中......