ArrayList源码分析
来源:互联网 发布:linux内核移植arm 编辑:程序博客网 时间:2024/06/10 05:18
ArrayList
源码基于1.8.0_112
ArrayList是最常用的集合之一,结构也比较简单。
原理 :ArrayList内部使用一个数组存储放入的数据,数组使用默认大小初始化(也可以自定义),当数组无法再放入更多的对象时,数组会扩大到原来的1.5倍。
成员变量
大致浏览,结合具体方法来了解具体含义
// 最重要的一个数组,所有存入ArrayList的对象都别存入该数组最重要的一个数组,所有存入ArrayList的对象都别存入该数组 // 使用transient,说明该数组不能被持久化,因为ArrayList实现了writeObject和readObject,使用了更有效的持久化方式 transient Object[] elementData; // elementData数组中实际存储元素的个数 private int size; // 默认的elementData数组初始化容量 private static final int DEFAULT_CAPACITY = 10; // list被修改的次数,该变量继承于AbstractList protected transient int modCount = 0; // 数组最大空间,某些虚拟机可能会预留一些数组空间存储元数据,所以空出8位 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 所有通过默认构造函数新建的ArrayList都会指向该空数组,static只有一份 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 所有通过非默认构造函数新建的ArrayList都会指向该空数组,static只有一份 private static final Object[] EMPTY_ELEMENTDATA = {};
构造方法
先阅读默认构造函数,其他构造函数可在阅读完具体操作后再回来阅读
/** * 默认构造方法 */ public MyArrayList() { // 指向一个默认的公用数组 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 指定数组容量 */ public MyArrayList(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); } }
add(E e)
每次加入对象前,会先进行数组大小检查,然后决定是否扩容
图解
代码
/** * 最常用的add方法 * * @param e * @return */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { // 选择参数或者默认容量中较大的一个作为数组初始化的大小 // DEFAULT_CAPACITY=10, DEFAULT_CAPACITY=1, 不可以直接取10? 个人估计 判读数组引用相等的速度 比 比较两个int大小的速度 要快 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { // list被修改,该值加1 modCount++; // elementData数组被填满时,需要对elementData数组进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 扩展原来大小的1.5倍,如果原来大小为10,则 10+(10/2)=15 int newCapacity = oldCapacity + (oldCapacity >> 1); // 32位int,溢出后为负值 // 这里有溢出的两种情况,具体会是那种不清楚 // -1-Integer.MAX_VALUE < 0 类似这种情况 if (newCapacity - minCapacity < 0) // 原本容量加1 newCapacity = minCapacity; // Integer.MIN_VALUE-1 > 0 类似这种情况 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 将原本数组复制到一个新的数组 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); // 如果数组容量已经超过最大容量,那么直接使用int最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
get(int index)
很简单,类似于数组的访问
代码
/** * get操作, 很简单,不需要过多解释 * @param index * @return */ public E get(int index) { rangeCheck(index); return elementData(index); } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } E elementData(int index) { return (E) elementData[index]; }
remove(Object o)
把删除位置之后的数组往前移动一位,并且将最后一位设为null
图解
/** * 常用的remove方法 * @param o * @return */ public boolean remove(Object o) { // 不管是否为null都是循环查找到相等的值,然后删除 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; } // 把删除位置之后的数组往前移动一位,并且将最后一位设为null // 例如,A,B,C,D --> A,C,D,null 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 }
iterator()
modCount是为了防止list在使用过程中被修改(其他的操作中也会用到),继承于AbstractList
之后的集合类分析中不在重复分析此方法
list返回迭代器的时候,新建一个内部的迭代器类。类初始的时候会记录modCount的值,每次进行next或者remove的时候都会检查初始化时记录的modCount,如果被修改则抛出异常。
代码
/** * 返回一个迭代器 * @return */ public Iterator<E> iterator() { return new MyArrayList.Itr(); } // 迭代器内部类 private class Itr implements Iterator<E> { int cursor; // 下一个返回对象的下标 int lastRet = -1; // 最近一次返回元素的下标,-1表示没有 // 初始化的时候记录modCount,每次操作前都会检查 int expectedModCount = modCount; @SuppressWarnings("unchecked") public E next() { // 检查modCount checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = MyArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); // 检查modCount checkForComodification(); try { MyArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; // 如果是迭代器内部修改,则重新记录modCount expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
writeObject和readObject
我们这里不分析这两个方法的实现,但是需要注意几点
1. 考虑到list可能被序列化,ArrayList实现了Serializable接口
2. elementData使用了transient关键字,所以他不会像其他成员变量一样被存储(使用其他的方式存储)
3. ArrayList中加入了writeObject和readObject,所以在序列化的时候会自动调用这两个方法
4. writeObject使用了更好的方式来存储elementData
5. 为了防止序列化时被修改writeObject也使用了modCount
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. 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; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not 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(); } } }
总结
- ArrayList中使用了数组,所以随机访问的性能很好
- 由于当内部数组容量不足时需要扩容,所以ArrayList的插入性能一般
- 当确定ArrayList会在短时间内超过某个容量时,可以直接指定初始化的容量,来避免频繁扩容带来的性能损失
0 0
- ArrayList源码分析
- ArrayList源码分析
- ArrayList 源码分析
- ArrayList源码分析
- ArrayList LinkedList 源码分析
- ArrayList,LinkedList源码分析
- 源码分析之ArrayList
- ArrayList 源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList 源码分析
- Java ArrayList源码分析
- ArrayList源码分析
- Java ArrayList 源码分析
- ArrayList的源码分析
- 各种软件教程地址
- ReactNative报出 'React/RCTBundleURLProvider.h' file not found错误
- ios
- javaweb七牛云切片视频+播放
- Android Material Design
- ArrayList源码分析
- LeetCode (27)Remove Element
- Xcode可重用代码块code snippets
- 安卓 “Handler” is abstract; cannot be instantiated 解决方法
- TCP/IP、Http的区别
- druid配置WebStatFilter完成网络url统计
- 教你快速拿到iOS应用中所有图片资源
- 如何用命令将本地项目上传到git
- qtcreator使用自定义的控件