ArrayList集合源码解读(二)已完结

ArrayList集合源码解读(二)

前言

这篇文章已经把 ArrayList 更完了。各位还想看什么源码可以私信我~~
上节课带大家阅读了 ArrayList 中的核心扩容代码,那么今天带大家阅读下List集合中我们常用的几个方法的底层实现逻辑!

常用方法解读

笔者为大家讲解list 集合类中常用的几种操作,咱们一起看看他们的底层源码是如何实现的。

大家要区分set()add()的区别,不要弄混了。

  • int size()返回此列表中的元素数。

  • boolean isEmpty()如果此列表为空,则返回 true 。

  • boolean contains(Object o)如果此列表包含指定的元素,则返回 true 。

  • int lastIndexOf(Object o)返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回 -1。

  • E get(int index)返回此列表中指定位置的元素。

  • E set(int index, E element)用指定的元素替换此列表中指定位置的元素。

  • boolean add(E e)将指定的元素追加到此列表的末尾。

  • void add(int index, E element) 在此列表中的指定位置插入指定的元素。

  • E remove(int index)删除该列表中指定位置的元素。 将任何后续元素向左移动一位(从其索引中减去一个元素)。

源码:

// 返回此列表中的元素个数 
public int size() {
     return size;
 }

// ------------------------------------

// 如果集合为空,返回 true
 public boolean isEmpty() {
     return size == 0;
 }

// ------------------------------------

/**
* 如果此列表包含指定元素,则返回true。更正式地说,当且仅当此列表包含至少一个元素value,使得Objects. 
* equals(o,value)时,返回true
*
* @param value 要测试其是否在此列表中的元素
* @return 如果此列表包含指定元素,返回true
*/
public boolean contains(Object value) {
    // contains 方法底层调用的是 indexOf(Object o) 中的 indexOfRange(Object o, int s, int e)
    return indexOf(value) >= 0;
}

// 底层方法实现
public int indexOf(Object o) {
    // 向下调用 indexOfRange() 方法,传入三个参数:需要查找的元素  查找起始索引  查找结束索引
    return indexOfRange(o, 0, size);
}

// 真正实现逻辑的方法
int indexOfRange(Object o, int start, int end) {
    // 1.获取当前集合中的元素  (ArrayList 底层是使用 Object[] 存储元素数据的)
    Object[] es = elementData;
    // 2.如果待查找的元素 o 为 null,则从 start 起始索引开始遍历到 end 结束索引,找到第一个等于 null 的元素后返回其索引值
    if (o == null) {
        for (int i = start; i < end; i++) {
            if (es[i] == null) {
                return i;
            }
        }
    } else {
        // 3.如果待查找的元素 o 不为 null,则则从 start 起始索引开始遍历到 end 结束索引,找到第一个等于 o 的元素后返回其索引值
        for (int i = start; i < end; i++) {
            if (o.equals(es[i])) {
                return i;
            }
        }
    }
    // 没找到的情况,返回 -1
    return -1;
}

// ------------------------------------

// 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回 -1。
public int lastIndexOf(Object o) {
    // 底层调用lastIndexOfRange() 来实现,传入三个参数:待查找的元素值 o,起始索引,结束索引
    return lastIndexOfRange(o, 0, size);
}

// 底层真正实现逻辑的方法
// 其实和上方的 indexOfRange() 方法类似,唯一变化的就是:
// indexOfRange() 是从开始索引遍历到结束索引;而 lastIndexOfRange() 是从结束索引倒序遍历。
int lastIndexOfRange(Object o, int start, int end) {
    Object[] es = elementData;
    if (o == null) {
        // 倒序遍历数组元素
        for (int i = end - 1; i >= start; i--) {
            if (es[i] == null) {
                return i;
            }
        }
    } else {
        // 倒序遍历数组元素
        for (int i = end - 1; i >= start; i--) {
            if (o.equals(es[i])) {
                return i;
            }
        }
    }
    return -1;
}

// ------------------------------------

/**
* 返回此列表中指定位置的元素。
*
* @param  index 要返回的元素的索引
* @return 此列表中指定位置的元素
* @throws IndexOutOfBoundsException 如果索引超出范围(索引<0||索引>=size())
*/
public E get(int index) {
    // 检查索引是否合法  如果 index < 0 || index >= size 那么抛出异常  (size 是集合中的元素个数)
    Objects.checkIndex(index, size);
    // 底层调用 elementData(int index) 方法实现
    return elementData(index);
}

// 底层真正实现逻辑的方法
@SuppressWarnings("unchecked")  // @SuppressWarnings() 注解的作用是让编译器忽略检查,不要飘黄线
E elementData(int index) {
    // 直接返回当前索引对应的元素
    return (E) elementData[index];
}

// ------------------------------------

/**
* 将此列表中指定位置的元素替换为指定元素。
*
* @param index 要替换的元素的索引
* @param element 要存储在指定位置的元素
* @return 先前位于指定位置的元素
* @throws IndexOutOfBoundsException 如果索引超出范围(索引<0||索引>=size())
*/
 public E set(int index, E element) {
     // 检查索引是否合法  如果 index < 0 || index >= size 那么抛出异常  (size 是集合中的元素个数)
     Objects.checkIndex(index, size);
     // 获取指定索引的元素,上面有调用,此次不重复讲解
     // 因为要返回原先处于该位置的元素,所以需要先用个变量保存
     E oldValue = elementData(index);
     // 替换掉旧元素
     elementData[index] = element;
     return oldValue;
 }

// ------------------------------------

/**
* 在此列表中的指定位置插入指定的元素。
* 先调用 rangeCheckForAdd 对index进行界限检查;然后调用 grow 方法保证 capacity  足够大;
* 再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
*/
 public void add(int index, E element) {
     // 检查索引是否合法 
     rangeCheckForAdd(index);
     // 版本号 +1
     // 此列表在结构上被修改的次数。结构修改是指改变列表大小的修改,或者以其他方式扰乱列表,使得正在进行的迭代可能会产生不正确的结果。
     modCount++;
     final int s;
     Object[] elementData;
     // 这句话的意思是 首先将 size 的值赋值给局部变量 s,再将数组底层存储数据的 Object[] elementData  赋值给局部变量 elementData。最好判断他们的长度是否一样
     if ((s = size) == (elementData = this.elementData).length) {
         // 扩容的核心方法 grow()  上面有讲,此处不再过多陈述
         elementData = grow();
     }
     // System.arraycopy() 数组拷贝方法很重要!!
     // 第一个参数是要复制的数组 a1
     // 第二个参数是从a1的哪个索引开始赋值
     // 第三个参数是要复制到哪个数组 a2
     // 第四个参数是从 a2 这个数组的哪个索引开始覆盖
     // 第五个参数是指复制多少个元素
     System.arraycopy(elementData, index,
                      elementData, index + 1,
                      s - index);
     elementData[index] = element;
     size = s + 1;
 }

// 判断索引是否符合规则  
// 和上方的 Objects.checkIndex() 方法的区别在于:
// rangeCheckForAdd() 方法判断的区间是 index < 0 || index > size
// checkIndex() 方法判断的区间是 index < 0 || index >= size
 private void rangeCheckForAdd(int index) {
     if (index > size || index < 0)
         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
 }
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值