源码分析

属性

// 默认的初始化容量
private static final int DEFAULT_CAPACITY = 10;
// 用于无参构造中初始化一个空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// ArrayList 是动态数组实现的,我们操作ArrayList 实际就是操作的这个对象
private transient Object[] elementData;
// 数组长度
private int size;

构造方法

// 无参构造,初始化一个没有元素的数组
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}

// 自定义初始化容量
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
}

// 创建一个包含 collection 的数组
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

public boolean add(E e):在数组末尾增加一个元素

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

扩充容量,设置尾部元素,数组长度自增。

返回值为 true。

public void add(int index, E element):在 index 位置插入元素

public void add(int index, E element) {
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}

1). 越界检测和容量扩充

2). index 位置之后的元素全部往后挪动一个单位

3). 为 index 位置填入元素,数组长度 +1

public boolean addAll(Collection<? extends E> c):在数组末尾添加一组元素

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;
}

简单不解释。只要数组改变就返回 true。

public boolean addAll(int index, Collection<? extends E> c):在 index 位置插入一组元素

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);
    int numMoved = 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;
}

类似 add(int index, E element)。只要数组有改变就返回 true。

E remove(int index):指定下标删除元素

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    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
    return oldValue;
}

1). 进行越界检查,修改 modCount

2). 将移除位置之后的元素全部往前挪动一个位置

3). 将最后一个位置的元素置为空同时将数组长度减一

4). 返回的对象是删除的元素

boolean remove(Object o):删除此元素

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;
}

1). 若此元素为空,顺序遍历数组,找到第一个也为空的元素并且删除,返回 true

2). 若此元素不为空,顺序遍历数组,找到相等的元素并且删除,返回 true

3). 若是找不到就会返回 false

4). 删除元素使用的方法是 faseRemove 如下:基本和 remove(int index) 一样的

private void fastRemove(int index):和 remove(int index) 一样

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):删除所有和 c 中元素相同的元素

public boolean removeAll(Collection<?> c) {
    return batchRemove(c, false);
}

调用 batchRemove 方法。遍历判断 c 是否包含元素。若包含,则不理会。若不包含就会依次存入该数组中。

返回值:只要集合有改变,就会返回 true。

下面的 retainAll 方法参考 removeAll。基本相同。

public boolean retainAll(Collection<?> c):删除所有和 c 中元素不同的元素

public boolean retainAll(Collection<?> c) {
    return batchRemove(c, true);
}

private boolean batchRemove(Collection<?> c, boolean complement):设计的挺巧妙的,一个方法多用。

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 {
        if (r != size) {
            System.arraycopy(elementData, r, elementData, w, size - r);
            w += size - r;
        }
        if (w != size) {
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

如果 w 不等于 size,说明此数组没有填充完。所以从 w 位置开始后面的都置为空。修改数组大小。

protected void removeRange(int fromIndex, int toIndex):删除两个位置之间的元素

protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

1). 修改 modCount,toIndex 之后的所有元素都向前挪动 numMoved 个单位

2). 最后面的 numMoved 个单位置为空,修改数组长度

public void clear():清空数组

public void clear() {
    modCount++;
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

所有位置的元素都置为空,数组长度改为 0。

public E set(int index, E element):替换掉 index 位置的元素

public E set(int index, E element) {
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

越界检测,获得 index 的旧元素并且设置上新元素。

返回旧元素。

public E get(int index)

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

越界检测,返回 index 位置的元素。

调整数组容量

// 扩充容量时会出现 size 很小而 length 很大的情况。出现空间浪费。使用这个方法将返回新的数组。size 和 length 相等。节省空间
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = Arrays.copyOf(elementData, size);
    }
}

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != EMPTY_ELEMENTDATA)? 0 : DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)    // 判断如果容量不足的时候再去扩充
        grow(minCapacity);
}
// 新容量将至少为旧容量的约 150%
// 扩容后的容量和自定义容量相比去较大值,再和最大容量相比去较小值。
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

其他方法

public Object clone():克隆 ArrayList

public Object clone() {
    try {
        ArrayList<E> v = (ArrayList<E>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        throw new InternalError();
    }
}

并没有克隆集合内的元素。

public boolean contains(Object o):判断集合是否包含元素 o

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}
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 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;
}

利用 indexof 定位元素。若能定位到就说明包含,否则就是不包含。

其他:

// 数组长度
public int size() {
    return size;
}

// 判断是否为空
public boolean isEmpty() {
    return size == 0;
}

// 集合转化为数组
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

总结

1). 基于数组实现,是一个动态数组,增加元素的时候会自行扩容。

  ArrayList 扩容后容量为旧容量的约 1.5 倍。而 Vector 扩容要看增长因子是否大于0,若大于0就增加增长因子这么大容量,否则扩容为旧容量的两倍。

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);    // Vector 扩容
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

2). 增加(插入)元素的时候,总是需要先将插入位置之后的元素往后挪动一个元素,再进行插入操作。同理删除需要往前挪。和 LinkedList 相比效率低。

3). 修改元素和删除元素可以直接利用下标进行操作。比 LinkedList 效率高。

4). 允许元素为 NULL。

5). 非同步非线程安全。而 Vector 是同步的线程安全的。线程同步必定会对性能造成影响,所以 ArrayList 要比 Vector 性能好一些。

6). fase-fail 机制:迭代器在面临并发修改时候,能够马上反馈失败,避免将来不确定的时间发生不确定行为的风险。

private class Itr implements Iterator<E> {
    int cursor;
    int lastRet = -1;
    int expectedModCount = modCount;    // 赋值

    public boolean hasNext() {
        return cursor != size;
    }

    public E next() {
        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];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();   // 迭代过程中进行判断
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

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

迭代器在初始化过程中会把 modCount 赋值给 exceptedModCount。在迭代器迭代的过程中如果发现这两个值不一样,就说明另外有线程对集合进行了修改,马上抛出异常。

Java 集合 - ArrayList的更多相关文章

  1. Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 [ 转载 ]

    Java 集合 ArrayList和LinkedList的几种循环遍历方式及性能对比分析 @author Trinea 原文链接:http://www.trinea.cn/android/arrayl ...

  2. Java基础系列 - JAVA集合ArrayList,Vector,HashMap,HashTable等使用

    package com.test4; import java.util.*; /** * JAVA集合ArrayList,Vector,HashMap,HashTable等使用 */ public c ...

  3. Java集合---ArrayList的实现原理

    目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除                 6) 调整数组容量 ...

  4. Java集合 -- ArrayList集合及应用

    JAVA集合 对象数组 集合类之ArrayList 学生管理系统 斗地主案例 NO.one 对象数组 1.1 对象数组描述 A:基本类型的数组:存储的元素为基本类型 int[] arr={1,2,3, ...

  5. Java集合--ArrayList出现同步问题的原因

    1 fail-fast简介 fail-fast 机制是java集合(Collection)中的一种错误机制.当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件.例如:当某一个线 ...

  6. java集合-- arraylist小员工项目

    import java.io.*; import java.util.ArrayList; public class Emexe { public static void main(String[] ...

  7. Java集合ArrayList的应用

    /** * * @author Administrator * 功能:Java集合类ArrayList的使用 */ package com.test; import java.io.BufferedR ...

  8. java集合-ArrayList

    一.ArrayList 概述 ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口 ...

  9. Java集合ArrayList源码解读

    最近在回顾数据结构,想到JDK这样好的代码资源不利用有点可惜,这是第一篇,花了心思.篇幅有点长,希望想看的朋友认真看下去,提出宝贵的意见.  :) 内部原理 ArrayList 的3个字段 priva ...

  10. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

随机推荐

  1. ios-系统警告框 跳转到设置里面

    -(void)createUIAlertIphone:(NSString*)title { UIAlertController * alert =[UIAlertController alertCon ...

  2. Asp.net DropDownList 自定义样式(想怎么改就怎么改!)

    最近在做一个asp.net的项目,需要对默认的dropdownlist样式进行美化,固有的dropdownlist的小箭头实在让人无法接受,于是开始在百度,google 上下求索,天不负有心人,终于找 ...

  3. C# Lock 解读[转]

    一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行, ...

  4. SharedPreferences第一次使用后HashMap将常驻内存

    今天使用SharedPreferences的时候突然想到了这个问题,因为我们要存储应用级别的上下文信息,包括用户信息等一系列信息:这个时候使用getSharedPreferences是否合适呢! 其实 ...

  5. How to prevent SQL injection attacks?

    In our earlier tutorial on SQL Injection, one way to have prevented the SQL injection attack was by ...

  6. LeetCode Alien Dictionary

    原题链接在这里:https://leetcode.com/problems/alien-dictionary/ 题目: There is a new alien language which uses ...

  7. PRAGMA AUTONOMOUS_TRANSACTION

    转自 http://blog.csdn.net/pan_tian/article/details/7675800 这段时间遇到一个问题,程序里明明插入了一条记录,但在后边的一段Procedure中却查 ...

  8. JAVASE02-Unit02: 正则表达式 、 Object 、 包装类

    正则表达式 . Object . 包装类 字符串支持正则表达式的方法一: package day02; /** * 字符串支持正则表达式的方法一: * boolean matches(String r ...

  9. python djang suit模板

    一.安装python3.django1.9 二.配置好项目环境,引入suit模板   python3 - m pip install django-suit==0.2.13 三.配置django后台s ...

  10. tableView和scrollView滚动起冲突

    tableView和scrollView滚动起冲突 tableView也是继承的scrollView,所以在滚动的时候也会触发scrollView的代理方法,在scrollViewDidScroll中 ...