集合(一)数组ArrayList 源码剖析

集合(一)数组ArrayList


1. 概述

ArrayList基于Object[],支持自动扩容实现的动态数组,日常开发中最常用的集合类。


2. 类图

ArrayList类图

继承了AbtractList类,实现了RandomAcess、Serializable、Cloneable和List,前三者是标记接口。

ArrayList重写了List的方法,因此继承AbstractList没发挥太多作用。


3. 属性

transient Object[] elementData; // 元素数组
private int size; // 实际存储元素个数

4. 构造方法

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}


public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
      	// JDK9解决
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

ArrayList为了节省内存,在首次添加元素时,才会真正初始化数组。

EMPTY_ELEMENTDATA从0开始扩容

而DEFAULTCAPACITY_EMPTY_ELEMENTDATA从10开始扩容


5. 添加元素

// 添加元素,在size位置添加
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
  	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
      return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
 
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}

// 1.5倍扩容,若不能容纳新增元素,则直接扩至容纳新增元素
// 数组扩容,核心方法
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
      newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
      newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}


// index位置添加元素
public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
		// index ~ size-1元素整体后移一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

// 添加多个元素,在size位置添加
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

// index位置添加多个元素
public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
		
  	// index ~ size-1元素整体后移size-index位
    if (numMoved > 0)
      System.arraycopy(elementData, index, elementData, index + numNew,
                       numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

6. 移除元素

// 按索引移除元素
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
  	// index+1 ~ size-1 整体前移一位
    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) {
    if (o == null) {
      for (int index = 0; index < size; index++)
        if (elementData[index] == null) {
          fastRemove(index);
          return true;
        }
    } else {
      for (int index = 0; index < size; index++)
        if (o.equals(elementData[index])) {
          fastRemove(index);
          return true;
        }
    }
    return false;
}

// 快速移除,无需检查数组范围
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 boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

// 求交集或差集,都是子集,数组原地替换,剩余的位置指向null
private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
      for (; r < size; r++)
        if (c.contains(elementData[r]) == complement)
          elementData[w++] = elementData[r];
    } finally {
      // Preserve behavioral compatibility with AbstractCollection,
      // even if c.contains() throws.
      if (r != size) {
        System.arraycopy(elementData, r,
                         elementData, w,
                         size - r);
        w += size - r;
      }
      if (w != size) {
        // clear to let GC do its work
        for (int i = w; i < size; i++)
          elementData[i] = null;
        modCount += size - w;
        size = w;
        modified = true;
      }
    }
    return modified;
}

// elementData与c的交集
public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
}

// elementData与c的差集
public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
}

// 清空数组,数组设置为null,size设置为0
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
      elementData[i] = null;

    size = 0;
}

7. 查找元素

// 顺序遍历查找元素
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;
}

// 是否包含元素
public boolean contains(Object o) {
        return indexOf(o) >= 0;
}

// 反序遍历查找元素
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;
}


E elementData(int index) {
  	return (E) elementData[index];
}

// 根据索引index获取元素
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}


// 设置索引index的值
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

// 拷贝一份数组
public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

// 拷贝一份泛型数组
public <T> T[] toArray(T[] a) {
    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);
    if (a.length > size)
      a[size] = null;
    return a;
}

8. 序列化和反序列化

序列化:将对象转换为字节

反序列化:将字节转换为对象

应用场景:网络传输和数据存储

transient修饰不会进行序列化和反序列化

没有定义serialVersionUID值, Java编译器会对jvm中该类的Class文件进行摘要算法生成一个

此时若修改类名,成员变量名会导致生成的serialVersionUID不同,导致反序列化失败。

因此建议固定一个serialVersionUID值

private void writeObject(java.io.ObjectOutputStream s)
  throws java.io.IOException{
  // 1. 写入非静态,非transient属性
  int expectedModCount = modCount;
  s.defaultWriteObject();

  // 2. 写入数组大小
	// ArrayList中size非静态,非transient,在一次被写入,重复写入size,有点奇怪
  // 在readObject() s.readInt(); 是忽略的
  s.writeInt(size);

  // 3. 顺序遍历,逐个序列化
  // ArrayList elmentData由transient修饰,按照实际大小,序列化数组元素
  for (int i=0; i<size; i++) {
    s.writeObject(elementData[i]);
  }

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

private void readObject(java.io.ObjectInputStream s)
  throws java.io.IOException, ClassNotFoundException {
  elementData = EMPTY_ELEMENTDATA;

  // 1. 读取非静态,非transient属性
  s.defaultReadObject();

  // 2. 读取数组大小
  s.readInt(); // ignored
	
  // 3. 顺序遍历,逐个反ß序列化
  // ArrayList elmentData由transient修饰,按照实际大小,反序列化数组元素
  // capacity和size相等
  if (size > 0) {
    // be like clone(), allocate array based upon size not capacity
    int capacity = calculateCapacity(elementData, size);
    SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
    ensureCapacityInternal(size);

    Object[] a = elementData;
    // Read in all elements in the proper order.
    for (int i=0; i<size; i++) {
      a[i] = s.readObject();
    }
  }
}

HashMap必须重写writeObject()、readObject(),因为序列化会导致字节流在不同的JVM中传输,而Entry的存放位置根据Ky的hash值计算,对于同一个key,不同的JVM会导致计算出来hash值不同,从而违背反序列化的基本要求——反序列化的对象与序列化之前的对象是一致的。

一般ArrayList的数据容量大于实际元素数量,ArrayList重写writeObject()、readObject(),,为了避免序列化没有元素的数组

序列化和clone()都产生新对象


9. 内部类

// AbstractList.Itr更优化的版本
private class Itr implements Iterator<E>;

// AbstractList.ListItr更优化的版本
private class ListItr extends Itr implements ListIterator<E>;

// 原数组切片
private class SubList extends AbstractList<E> implements RandomAccess;

// 数组中可以分割的迭代器
static final class ArrayListSpliterator<E> implements Spliterator<E>

10. 小结

ArrayList随机访问快,随机访问for i比顺序访问for each更快

LinkedList增加、删除元素快。

ArrayList支持自动扩容

Redis String底层数据结构类似于Java ArrayList


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值