java集合中ArrayList源码解析是怎样的
今天就跟大家聊聊有关java集合中ArrayList源码解析是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
成都创新互联公司服务项目包括循化网站建设、循化网站制作、循化网页制作以及循化网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,循化网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到循化省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
整体架构
ArrayList实际就是个数组结构,如图
index:数组下标
elementData:数组本身
其他基本概念:
/** * Default initial capacity. * 数组初始大小 */ private static final int DEFAULT_CAPACITY = 10;
/** * The size of the ArrayList (the number of elements it contains). * 当前数组大小 * @serial */ private int size;
//表示当前数组被修改的版本次数 protected transient int modCount = 0;
类注释:
1、可以自动扩容
2、允许put null
3、size、set、put、add、get时间复杂度O(1)
4、非线程安全(作为共享变量时存在),必要时可以使用线程安全的SynchronizedList(性能低)
public boolean add(E e) { synchronized (mutex) {// synchronized 是一种轻量锁,mutex 表示一个当前 SynchronizedList return c.add(e); } }
5、增强for循环或迭代时,若数组大小改变,则抛出异常
源码解析
一、初始化:
//1、指定大小初始化 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); } } //2、无参初始化 数组大小为空//初始化时数组大小不是默认的10,第一次add时扩容为10public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //3、指定初始数据初始化public ArrayList(Collection extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
二、新增、扩容、删除
新增:
public boolean add(E e) { //确保数组大小是否足够,不够执行扩容,size 为当前数组的大小 ensureCapacityInternal(size + 1); // Increments modCount!! //直接赋值,线程不安全的 elementData[size++] = e; 对null无校验,因此可以存入null值 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 的意思 这里注意:ArrayList扩容是1+0.5倍 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); }
扩容动态图:
删除:
public boolean remove(Object o) { // 如果要删除的值是 null,找到第一个值是 null 的删除 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { 可以删除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; }
三、迭代器
implement java.util.Iterator
迭代器的一些参数:
int cursor;// 迭代过程中,下一个元素的位置,默认从 0 开始。
int lastRet = -1; // 新增场景:表示上一次迭代过程中,索引的位置;删除场景:为 -1。
int expectedModCount = modCount;// expectedModCount 表示迭代过程中,期望的版本号;modCount 表示数组实际的版本号。
迭代器源码分析:
//是否存在下一个可迭代的值 public boolean hasNext() { return cursor != size;//cursor 表示下一个元素的位置,size 表示实际大小,如果两者相等,说明已经没有元素可以迭代了,如果不等,说明还可以迭代 } //下一个可迭代的值 public E next() { //迭代过程中,判断版本号有无被修改,有被修改,抛 ConcurrentModificationException 异常 checkForComodification(); //本次迭代过程中,元素的索引位置 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); // 下一次迭代时,元素的位置,为下一次迭代做准备 cursor = i + 1; // 返回元素值 return (E) elementData[lastRet = i]; } // 版本号比较 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } public void remove() { // 如果上一次操作时,数组的位置已经小于 0 了,说明数组已经被删除完了 if (lastRet < 0) throw new IllegalStateException(); //迭代过程中,判断版本号有无被修改,有被修改,抛 ConcurrentModificationException 异常 checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; // -1 表示元素已经被删除,这里也防止重复删除lastRet = -1; // 删除元素时 modCount 的值已经发生变化,在此赋值给 expectedModCount // 这样下次迭代时,两者的值是一致的了 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
看完上述内容,你们对java集合中ArrayList源码解析是怎样的有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。
文章标题:java集合中ArrayList源码解析是怎样的
分享URL:http://scyanting.com/article/jdhhgj.html