深入学习ArrayList

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Woo_home/article/details/103177912
            </div>
                                                <!--一个博主专栏付费入口-->
         
         <!--一个博主专栏付费入口结束-->
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
                                    <div id="content_views" class="markdown_views prism-tomorrow-night">
                <!-- flowchart 箭头图标 勿删 -->
                <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                    <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                </svg>
                                        <h2><a name="t0"></a><a id="ArrayList_0"></a>什么是ArrayList?</h2>

ArrayList的实现原理其实就是数组(动态数组),ArrayList的介绍及简单使用方法

动态数组与一般数组有什么区别?

与Java中的数组相比,ArrayList的容量能动态地增长

ArrayList效率怎么样?

ArrayList不是线程安全的,所以效率比较高 ,但是只能用于单线程的环境中,那多线程呢?别急,文末会讲到

ArrayList主要继承哪些类实现了哪些接口?

ArrayList主要继承了AbstractList类,实现了List、RandomAccess、Cloneable、Serializable接口

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 
 
  • 1
  • 2

RandomAccess的意思是其拥有快速访问的能力,ArrayList可以以 O(1)[^1]的时间复杂度去根据下标访问元素。由于ArrayList底层机构是数组,所以它占据了一块连续的内存空间,其长度就是数组的大小,因此它也有数组的缺点,在空间效率不高,但是也有它的有点,就是查询速度快,时间效率较快

ArrayList的常量与变量有哪些?

// 序列ID
private static final long serialVersionUID = 8683452581122892189L;

// ArrayList默认的初始容量大小
private static final int DEFAULT_CAPACITY = 10;

// 空对象数组,用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};

// 空对象数组,如果使用默认的构造函数创建,则默认对象内容是该值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存放当前数据,不参与序列化
transient Object[] elementData; // non-private to simplify nested class access

// list大小
private int size;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

当集合中的元素超出数组规定的长度时,数组就会进行扩容操作,扩容操作就是ArrayList存储操作缓慢的原因,尤其是当数据量较大的时候,每次扩容消耗的时间会越来越多

ArrayList的构造方法有哪些?

一、ArrayList(int initialCapacity)

所以当我们要使用ArrayList时,可以 new ArrayList(大小)构造方法来指定集合的大小,以减少扩容的次数,提高写入效率,该构造函数的源码如下:

// 自定义初始容量的构造方法
public ArrayList(int initialCapacity) {
<span class="token keyword">if</span> <span class="token punctuation">(</span>initialCapacity <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>elementData <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span>initialCapacity<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>initialCapacity <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>elementData <span class="token operator">=</span> EMPTY_ELEMENTDATA<span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token comment">// 如果初始容量小于0,则会出现 IllegalArgumentException 异常</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Illegal Capacity: "</span><span class="token operator">+</span>
                                       initialCapacity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这个构造函数还是比较好理解的,因为涉及到的代码也不多,而且都是一些基础的代码,相信聪明的你肯定看得懂的

二、ArrayList()

这个就更简单了,只有两行代码

// 默认的构造方法,构造一个初始容量为10的空列表
public ArrayList() {
    // elementData 初始化为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

三、ArrayList(Collection<? extends E> c)

// 构造一个包含指定元素的列表集合,按集合的返回顺序迭代器
// 传入参数为Collection对象
// c要将其元素放入此列表的集合
public ArrayList(Collection<? extends E> c) {
<span class="token comment">// 调用toArray()方法将Collection对象转换为Object[]</span>
elementData <span class="token operator">=</span> c<span class="token punctuation">.</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 判断size的大小,如果size值为0,则会抛出NullPointerException异常</span>
<span class="token comment">// 如果size &gt; 0 ,则执行以下代码</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>size <span class="token operator">=</span> elementData<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// c.toArray might (incorrectly) not return Object[] (see 6260652)</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>elementData<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
        
        <span class="token comment">// 执行Arrays.copyOf,把Collection对象的内容copy到elementData中</span>
        elementData <span class="token operator">=</span> Arrays<span class="token punctuation">.</span><span class="token function">copyOf</span><span class="token punctuation">(</span>elementData<span class="token punctuation">,</span> size<span class="token punctuation">,</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token comment">// replace with empty array.</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>elementData <span class="token operator">=</span> EMPTY_ELEMENTDATA<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

ArrayList的方法有哪些?

add()

单个add()
// 添加单个元素,添加元素之前会先检查容量,如果容量不足则调用grow方法
public boolean add(E e) {
    // 判断添加后的长度是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 然后在数组末尾添加当前元素,并且修改size的大小
    elementData[size++] = e;
    // 返回布尔值true
    return true;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

add()方法中主要用到了一个新的方法——ensureCapacityInternal,来看下ensureCapacityInternal的源码:

// 判断是否需要扩容
private void ensureCapacityInternal(int minCapacity) {
    // 执行 calculateCapacity
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

而 ensureCapacityInternal 主要调用的是 ensureExplicitCapacity 方法和 calculateCapacity 方法,我们先看下calculateCapacity 方法

// 判断是否是第一次初始化数组
private static int calculateCapacity(Object[] elementData, int minCapacity) {
<span class="token comment">// 判断当前数组是否等于空的数组</span>
<span class="token comment">// 注意:这里的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 并不是 EMPTY_ELEMENTDATA,不过并无太大差别,只是为了		       区分何时需要扩容而已</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>elementData <span class="token operator">==</span> DEFAULTCAPACITY_EMPTY_ELEMENTDATA<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    
    <span class="token comment">// 取其中最大的值作为判断本次是否需要扩容的依据,由于第一次数组是空的,所以默认要使数组扩容到10的长度</span>
    <span class="token keyword">return</span> Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>DEFAULT_CAPACITY<span class="token punctuation">,</span> minCapacity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> minCapacity<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

点击ensureCapacityInternal中的ensureExplicitCapacity,可以看到来到了 ensureExplicitCapacity 方法,而 ensureExplicitCapacity 主要调用的就是上面所说的 grow 方法,源码如下:

// 判断扩容的方法
private void ensureExplicitCapacity(int minCapacity) {
<span class="token comment">// 如果需要扩容modCount自增,这个参数是指当前列表的结构被修改的次数</span>
modCount<span class="token operator">++</span><span class="token punctuation">;</span>

<span class="token comment">// overflow-conscious code</span>
<span class="token comment">// 判断当前数据量是否大于数组的长度,如果是,进行扩容</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">-</span> elementData<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token comment">// 执行扩容操作</span>
    <span class="token function">grow</span><span class="token punctuation">(</span>minCapacity<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

grow方法源码如下:

// grow扩容方法
private void grow(int minCapacity) {
    // overflow-conscious code
    // 记录扩容前的数组长度
    int oldCapacity = elementData.length;
<span class="token comment">// 将原数组的长度扩大0.5倍作为扩容后数组的长度(如果扩容钱数组长度为10,那么经过扩容后的数组长度应该为15)</span>
<span class="token comment">// 这里涉及到异或运算,不懂的朋友可以看下这篇文章 https://blog.csdn.net/Woo_home/article/details/103146845</span>
<span class="token keyword">int</span> newCapacity <span class="token operator">=</span> oldCapacity <span class="token operator">+</span> <span class="token punctuation">(</span>oldCapacity <span class="token operator">&gt;&gt;</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 如果扩容后的长度小于当前的数据量</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>newCapacity <span class="token operator">-</span> minCapacity <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token comment">//那么就将当前的数据量的长度作为本次扩容的长度</span>
    newCapacity <span class="token operator">=</span> minCapacity<span class="token punctuation">;</span>

<span class="token comment">// 判断新数组的长度是否大于可分配数组的最大值</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>newCapacity <span class="token operator">-</span> MAX_ARRAY_SIZE <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token comment">// 将扩容长度设置为最大可用长度</span>
    newCapacity <span class="token operator">=</span> <span class="token function">hugeCapacity</span><span class="token punctuation">(</span>minCapacity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// minCapacity is usually close to size, so this is a win:</span>
<span class="token comment">// 拷贝,扩容,构建一个新的数组</span>
elementData <span class="token operator">=</span> Arrays<span class="token punctuation">.</span><span class="token function">copyOf</span><span class="token punctuation">(</span>elementData<span class="token punctuation">,</span> newCapacity<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

grow方法调用的hugeCapacity源码如下:

//如果新数组长度超过当前数组定义的最大长度时
private static int hugeCapacity(int minCapacity) {
<span class="token comment">// 抛出OOM异常</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// overflow</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">OutOfMemoryError</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 将扩容长度设置为Interger.MAX_VALUE,也就是int的最大长度</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>minCapacity <span class="token operator">&gt;</span> MAX_ARRAY_SIZE<span class="token punctuation">)</span> <span class="token operator">?</span>
    Integer<span class="token punctuation">.</span>MAX_VALUE <span class="token operator">:</span>
MAX_ARRAY_SIZE<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
指定add()
public void add(int index, E element) {
    //判断下标是否越界,如果是则抛出IndexOutOfBoundsException异常
    rangeCheckForAdd(index);
<span class="token comment">// 判断是否需要扩容,上面讲到过,这里不再解释</span>
<span class="token function">ensureCapacityInternal</span><span class="token punctuation">(</span>size <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// Increments modCount!!</span>

<span class="token comment">// 拷贝数组,将下标后面的元素全部向后移动一位</span>
System<span class="token punctuation">.</span><span class="token function">arraycopy</span><span class="token punctuation">(</span>elementData<span class="token punctuation">,</span> index<span class="token punctuation">,</span> elementData<span class="token punctuation">,</span> index <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>
                 size <span class="token operator">-</span> index<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 将元素插入到当前下标的位置</span>
elementData<span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token operator">=</span> element<span class="token punctuation">;</span>
size<span class="token operator">++</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

rangeCheckForAdd方法

// 判断下标是否越界,如果是则抛出IndexOutOfBoundsException异常
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
添加多个元素addAll()
// 添加多个元素
public boolean addAll(Collection<? extends E> c) {
    return addAll(this.size, c);
}

// 添加多个元素到指定下标
public boolean addAll(int index, Collection<? extends E> c) {

<span class="token comment">// 判断下标是否越界,上面提到过</span>
<span class="token function">rangeCheckForAdd</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 判断c的大小是否大于0</span>
<span class="token keyword">int</span> cSize <span class="token operator">=</span> c<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 如果等于0 返回 false</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>cSize<span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

<span class="token function">checkForComodification</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 将元素插入到数组中</span>
parent<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>parentOffset <span class="token operator">+</span> index<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 将修改次数赋值给 modCount</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>modCount <span class="token operator">=</span> parent<span class="token punctuation">.</span>modCount<span class="token punctuation">;</span>

<span class="token comment">// size大小加一</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>size <span class="token operator">+=</span> cSize<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
private void checkForComodification() {
    // 如果修改的次数不相等
    if (ArrayList.this.modCount != this.modCount)
        // 则抛出ConcurrentModificationException(并发修改)异常
        throw new ConcurrentModificationException();
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

总结:

在进行 add 操作时先判断下标是否越界,是否需要扩容,如果需要扩容,就复制数组,然后设置对应的下标元素值

扩容:默认扩容一半,如果扩容一半不够的话,就用目标的size作为扩容后的容量

get()

// 先判断下标索引
public E get(int index) {
    // 调用rangeCheck判断是否超出了Object数组长度
    rangeCheck(index);
<span class="token comment">// 调用 elementData 方法</span>
<span class="token keyword">return</span> <span class="token function">elementData</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
private void rangeCheck(int index) {
    // 如果超出了Object数组的长度
    if (index >= size)
        // 则抛出 IndexOutOfBoundsException(数组下标越界)异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
// 通过下标索引找到对应的元素值,返回指定元素
E elementData(int index) {
    return (E) elementData[index];
}

 
 
  • 1
  • 2
  • 3
  • 4

set()

public E set(int index, E element) {
    // 调用rangeCheck判断是否超出范围,上面讲到过,不懂的同学往上翻翻
    rangeCheck(index);
<span class="token comment">// 返回指定元素,上面也讲到过</span>
E oldValue <span class="token operator">=</span> <span class="token function">elementData</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>
elementData<span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token operator">=</span> element<span class="token punctuation">;</span>
<span class="token keyword">return</span> oldValue<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

remove()

// 删除元素
public E remove(int index) {
    // 调用rangeCheck方法判断是否超出范围,上面讲到过
    rangeCheck(index);
modCount<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token comment">// 位置访问操作</span>
E oldValue <span class="token operator">=</span> <span class="token function">elementData</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 计算移除元素后需要移动的元素个数</span>
<span class="token keyword">int</span> numMoved <span class="token operator">=</span> size <span class="token operator">-</span> index <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>numMoved <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token comment">// 通过 System.arraycopy 方法将后面的元素往前移动一位</span>
    System<span class="token punctuation">.</span><span class="token function">arraycopy</span><span class="token punctuation">(</span>elementData<span class="token punctuation">,</span> index<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">,</span> elementData<span class="token punctuation">,</span> index<span class="token punctuation">,</span>
                     numMoved<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 最后一位赋值为null</span>
elementData<span class="token punctuation">[</span><span class="token operator">--</span>size<span class="token punctuation">]</span> <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token comment">// clear to let GC do its work</span>

<span class="token comment">// 返回移除后元素的值</span>
<span class="token keyword">return</span> oldValue<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
// 删除对象
public boolean remove(Object o) {
    // 如果对象为null
    if (o == null) {
        // 遍历整个list去匹配移除的值
        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;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

fastRemove源码如下:

/**
  * 私有删除方法,跳过边界检查并且不返回删除的值。
  */
private void fastRemove(int index) {
    modCount++;
    // 位置访问操作
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 通过 System.arraycopy 方法将后面的元素往前移动一位
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Iterator

// ArrayList中的迭代器
public Iterator<E> iterator() {
    // 主要返回一个Itr类
    return new Itr();
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

Itr类源码如下:

private class Itr implements Iterator<E> {
    int cursor;       // 下一个要返回的元素的索引
    int lastRet = -1; // 返回的最后一个元素的索引; -1(如果没有)
    int expectedModCount = modCount;
<span class="token function">Itr</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token comment">// 判断是否还有下一个元素</span>
<span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 通过判断以下一个下标是否为数组大小,返回布尔值</span>
    <span class="token keyword">return</span> cursor <span class="token operator">!=</span> size<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token annotation punctuation">@SuppressWarnings</span><span class="token punctuation">(</span><span class="token string">"unchecked"</span><span class="token punctuation">)</span>
<span class="token comment">// 获取下一个元素</span>
<span class="token keyword">public</span> E <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 调用checkForComodification方法检查修改的次数是否一致</span>
    <span class="token function">checkForComodification</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// 定义下一个元素的下标</span>
    <span class="token keyword">int</span> i <span class="token operator">=</span> cursor<span class="token punctuation">;</span>
    
    <span class="token comment">// 判断下标,如果下标大于ArrayList包含的元素个数</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">&gt;=</span> size<span class="token punctuation">)</span>
        <span class="token comment">// 抛出 NoSuchElementException (没有这样的元素异常)异常</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">NoSuchElementException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// 定义elementData 为ArrayList的数组</span>
    Object<span class="token punctuation">[</span><span class="token punctuation">]</span> elementData <span class="token operator">=</span> ArrayList<span class="token punctuation">.</span><span class="token keyword">this</span><span class="token punctuation">.</span>elementData<span class="token punctuation">;</span>
    
    <span class="token comment">// 再次判断下标,如果此次判断不一致则说明数组被修改过</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">&gt;=</span> elementData<span class="token punctuation">.</span>length<span class="token punctuation">)</span>
        <span class="token comment">// 抛出 ConcurrentModificationException (并发修改)异常</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentModificationException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// 定义下一个元素的下标</span>
    cursor <span class="token operator">=</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
    
    <span class="token comment">// 将lastRet定义为下一个元素的下标(返回的最后一个元素的下标),然后返回下标对应的值</span>
    <span class="token keyword">return</span> <span class="token punctuation">(</span>E<span class="token punctuation">)</span> elementData<span class="token punctuation">[</span>lastRet <span class="token operator">=</span> i<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 移除当前元素</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 如果没有元素</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>lastRet <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token comment">// 则抛出 IllegalStateException 异常</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 又来了,调用 checkForComodification,上面讲到过,用于判断修改次数是否一致</span>
    <span class="token function">checkForComodification</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token comment">// 调用ArrayList的remove方法</span>
        <span class="token comment">//如果在遍历外remove会导致Itr中的expectedModCount没有修改则抛出异常</span>
        ArrayList<span class="token punctuation">.</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>lastRet<span class="token punctuation">)</span><span class="token punctuation">;</span>
        
        <span class="token comment">// 定义下一个元素的下标为当前下标</span>
        cursor <span class="token operator">=</span> lastRet<span class="token punctuation">;</span>
        
        <span class="token comment">// 定义上个遍历下标为-1</span>
        lastRet <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
        expectedModCount <span class="token operator">=</span> modCount<span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IndexOutOfBoundsException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentModificationException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
final void checkForComodification() {
    // 判断当前Itr修改次数和ArrayList是否一致
    if (modCount != expectedModCount)
        // 不一致则抛出ConcurrentModificationException(并发修改异常)异常
        throw new ConcurrentModificationException();
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

ArrayList是线程安全的吗?

不,ArrayList不是线程安全的,而且ArrayList允许元素为null

ArrayList如何实现线程安全?

上上面说过ArrayList不是线程安全的,所以效率较高,但是只能适用于单线程,那么多线程怎么办呢?, 多线程可以使用**Collections.synchronizedList(List list) **函数返回一个线程安全的 ArrayList 集合,或者使用 concurrent 并发包下的CopyOnWriteArrayList,如下:

//使用Collections.synchronizedList(List list)方法实现线程安全
List<?> list = Collections.synchronizedList(new ArrayList<>());

 
 
  • 1
  • 2
                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
                </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值