Java集合源码(ArrayList)-笔记

ArrayList

初始化
  • 无参:默认大小为空数组,10是第一次扩容的大小
  • 指定大小
  • 指定初始数据:
public ArrayList(Collection<? extends E> c) {
     
    //elementData 是保存数组的容器,默认为 null
    elementData = c.toArray();
    //如果给定的集合(c)数据有值
    if ((size = elementData.length) != 0) {
     
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        //如果集合元素类型不是 Object 类型,我们会转成 Object
        if (elementData.getClass() != Object[].class) {
     
            elementData = Arrays.copyOf(elementData, size, Object[].class);
        }
    } else {
     
        // 给定集合(c)无值,则默认空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

jdk8一些小bug:
有初始数据初始化的时候,元素类型不是Object类型的会被转成Object类型。
背景:ArrayList 初始化之后(ArrayList 元素非 Object 类型),再次调用 toArray 方法,得到 Object 数组,并且往 Object 数组赋值时,才会触发此 bug
Java集合源码(ArrayList)-笔记_第1张图片

新增
public boolean add(E e) {
     
  //确保数组大小是否足够,不够执行扩容,size 为当前数组的大小
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  //直接赋值,线程不安全的
  elementData[size++] = e;
  return true;
}
private void ensureCapacityInternal(int minCapacity) {
     
  //如果初始化数组大小时,有给定初始值,以给定的大小为准,不走 if 逻辑
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     
    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  }
  //确保容积足够
  ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
     
  //记录数组被修改
  modCount++;
  // 如果我们期望的最小容量大于目前数组的长度,那么就扩容
  if (minCapacity - elementData.length > 0)
    grow(minCapacity);
}
//扩容,并把现有数据拷贝到新的数组里面去
private void grow(int minCapacity) {
     
  int oldCapacity = elementData.length;
  // oldCapacity >> 1 是把 oldCapacity 除以 2 的意思
  int newCapacity = oldCapacity + (oldCapacity >> 1);

  // 如果扩容后的值 < 我们的期望值,扩容后的值就等于我们的期望值
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;

  // 如果扩容后的值 > jvm 所能分配的数组的最大值,那么就用 Integer 的最大值
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
 
  // 通过复制进行扩容
  elementData = Arrays.copyOf(elementData, newCapacity);
}
总结
  1. 赋值前确保数组大小够不够->确认容积够不够->扩容?
  2. 扩容后是原来的1.5倍
  3. 最大值是Integer.MAX_VALUE,超过则jvm不分配空间
  4. 允许null
删除
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 {
     
    // 如果要删除的值不为 null,找到第一个和要删除的值相等的删除
    for (int index = 0; index < size; index++)
      // 这里是根据  equals 来判断值相等的,相等后再根据索引位置进行删除
      if (o.equals(elementData[index])) {
     
        fastRemove(index);
        return true;
      }
  }
  return false;
}
private void fastRemove(int index) {
     
  // 记录数组的结构要发生变动了
  modCount++;
  // numMoved 表示删除 index 位置的元素后,需要从 index 后移动多少个元素到前面去
  // 减 1 的原因,是因为 size 从 1 开始算起,index 从 0开始算起
  int numMoved = size - index - 1;
  if (numMoved > 0)
    // 从 index +1 位置开始被拷贝,拷贝的起始位置是 index,长度是 numMoved
    System.arraycopy(elementData, index+1, elementData, index, numMoved);
  //数组最后一个位置赋值 null,帮助 GC
  elementData[--size] = null;
}
总结
  1. 从头开始用equals比较,非基本类型注意equals的实现
  2. 找到对应位置,将该位置后面的元素全部前移,并将末位的值设置为null,方便gc,–size。
  3. 如此重复,直至遍历到index=size-1结束

Source code from imooc

你可能感兴趣的:(后端,java,源码)