ArrayList

一、定义

(一)概要

   ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了Collection和List接口,灵活的设置数组的大小等好处。

(二)继承关系
// 类继承关系
java.lang.Object
   java.util.AbstractCollection<E>
       java.util.AbstractList<E>
          java.util.ArrayList<E>

// 所有已实现的接口
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess

// 直接已知子类
AttributeList, RoleList, RoleUnresolvedList
(三)类图

                 

二、常用方法

(一)构造方法

// 构造一个初始容量为 10 的空列表
ArrayList()

// 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的
ArrayList(Collection<? extends E> c)

// 构造一个具有指定初始容量的空列表
ArrayList(int initialCapacity)
(二)方法

// 将指定的元素添加到此列表的尾部
boolean	add(E e) 

// 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部
boolean	addAll(Collection<? extends E> c)

// 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中
boolean	addAll(int index, Collection<? extends E> c)

// 移除此列表中的所有元素
void clear()

// 返回此 ArrayList 实例的浅表副本
Object clone()

// 如果此列表中包含指定的元素,则返回 true
boolean	contains(Object o)

// 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数
void ensureCapacity(int minCapacity)

// 返回此列表中指定位置上的元素
E get(int index)

// 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
int indexOf(Object o)

// 如果此列表中没有元素,则返回 true
boolean	isEmpty()

// 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1
int lastIndexOf(Object o)

// 移除此列表中指定位置上的元素
E remove(int index)

// 移除此列表中首次出现的指定元素(如果存在)
boolean	remove(Object o) 

// 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
protected void removeRange(int fromIndex, int toIndex)

// 用指定的元素替代此列表中指定位置上的元素
E set(int index, E element)

// 返回此列表中的元素数
int size()

// 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
Object[] toArray()

// 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型
<T> T[]	toArray(T[] a)

// 将此 ArrayList 实例的容量调整为列表的当前大小
void trimToSize()



三、源码解析(JDK1.6.0_20)

1.定义成员变量

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

// 定义底层数组
private transient Object[] elementData;

// 定义集合大小
private int size;

2.定义构造函数

// 默认无参构造函数,其实调用的是有参构造函数
public ArrayList() {
    this(10);
}

// 指定集合大小有参构造函数
public ArrayList(int initialCapacity) {
    super();
    	
    if (initialCapacity < 0){
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
            
    his.elementData = new Object[initialCapacity];
}

// 入参为集合类型的构造函数
public ArrayList(Collection<? extends E> c) {
    // 将集合转为数组
    elementData = c.toArray();
    // 给集合大小赋值    
    size = elementData.length;
    // c.toArray返回的不一定是Object[]类型数组(比如:List<String> list = Arrays.asList("abc");)
    if (elementData.getClass() != Object[].class) {
        elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
}

3.扩容和新增元素

     

// 数组扩容
public void ensureCapacity(int minCapacity) {
    // 增加集合修改次数
    modCount++;
    // 原有集合长度
    int oldCapacity = elementData.length;

    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        // 每次扩容为本身的0.5倍
        int newCapacity = (oldCapacity * 3)/2 + 1;
        // 如果原有集合本身小,然后一次新增元素很多则长度为:原有集合长度+新增集合长度
    	if (newCapacity < minCapacity){
            newCapacity = minCapacity;
        }
        // 数组扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

// 增加单个元素
public boolean add(E e) {
    // 扩容
    ensureCapacity(size + 1);
    // 在新增元素放入输入指定位置
    elementData[size++] = e;

    return true;
}

// 指定位置增加元素
public void add(int index, E element) {
    if (index > size || index < 0) {
        throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
    }
    ensureCapacity(size+1);  // 扩容
    // 将原有数组指定位置到数组最后一个元素,整体向后面移动一个位置
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    // 将新增元素放入指定位置
    elementData[index] = element;

    size++;
 }

// 将集合作为参数,新增至原有集合
public boolean addAll(Collection<? extends E> c) {
    // 将集合变为数组
    Object[] a = c.toArray();
    // 传入数组长度
    int numNew = a.length;
    // 扩容
    ensureCapacity(size + numNew);  // 注意这里传入的是原有数组和新增数组长度之和
    System.arraycopy(a, 0, elementData, size, numNew); // 将新增数组放入原有数组后面(从最后一个元素开始)
    // 原有集合大小增加
    size += numNew;

    return numNew != 0;
}

4.移除、替换、获取元素

// 删除指定位置元素
public E remove(int index) {
    // 效验移除的元素是否存在
    RangeCheck(index);
    // 增加集合修改次数
    modCount++;
    // 移除的目标元素
    E oldValue = (E) elementData[index];
    // 集合指定下标元素后面元素的个数
    int numMoved = size - index - 1;
    // 将指定元素后面所有的元素向前面移动一个位置
    if (numMoved > 0){
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    } 
    // 将数组最后一个元素置空
    elementData[--size] = null; 

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

// 清除所有的元素
public void clear() {
    modCount++;
    // 移除所有元素,并将集合大小置为0
    for (int i = 0; i < size; i++)
        elementData[i] = null;	
    size = 0;
}

// 将指定位置元素替换
public E set(int index, E element) {
    RangeCheck(index);// 参数效验
    // 获取指定下标元素
    E oldValue = (E) elementData[index];
    // 将指定下标元素替换为element	
    elementData[index] = element;
	
    return oldValue;
}

// 获取指定下标位置元素
public E get(int index) {
    RangeCheck(index);

    return (E) elementData[index];// 返回指定下标元素
}

5.其他

// 将集合大小调整为实际元素个数
public void trimToSize() {
	    modCount++;
	    // 底层数组长度
	    int oldCapacity = elementData.length;
	    // 将集合大小置为实际元素个数大小
	    if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
	    }
    }

// 返回集合元素个数
public int size() {

    return size;
}

// 判断元素个数是否为0
public boolean isEmpty() {
    return size == 0;
}

// 判断指定元素是否存在
 public boolean contains(Object o) {
    return indexOf(o) >= 0;
 }

// 返回指定元素下标,不存在则返回-1
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;
}

// 浅克隆目标集合
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) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
}

// 将集合转为数组
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;
}


四、总结

1.ArrayList底层为一个动态的数组,通过操作数组来实现各种操作的,在新建一个ArrayList集合时候最好根据实际使用情况初始化集合的大小,避免频繁的扩容影响程序性能。

2.将数组转为集合时候,尽量不要使用List list = Arrays.asList(array),这样获取的集合在list.toAarry()后可能获取的不是Object[],当写入集合时类型不是底层数组保存的类型时候会报错。而采用List list = new ArryList(Arrays.asList(array))这样的形式,这样在ArryList参数为Collection类型的构造函数里面会将底层数组类型置为Object[]。

3.ArrayList不是同步的,如果需要同步请采用Vector、Collections类中提供的静态工厂方法创建的类或者考虑使用并发容器类CopyOnWriteArrayList。

4.其抽象类AbstractList已经实现了Iterable接口,并返回了一个Iterator接口类型的直接实现类如:Iterator iter = list.iterator();可以通过迭代器实现ArrayList集合的遍历(遍历时候不能通过list引用本身对集合进行操作,但是可以通过迭代器),也可以通过新for循环和传统for循环进行遍历。

5.大家在阅读源码时候有一个参数modCount(集合修改次数),这个参数是在ArrayList父类AbstractList中定义的,有什么用?我们知道ArrayList的迭代器也是在其父类AbstractList中实现的,我们在迭代遍历时候如果在多线程环境下有其他线程改变了这个list那么迭代器会抛出ConcurrentModificationException异常,怎么抛出的呢?就是在调用next()、remove()等方法时候会去检查这个modCount参数是否被改变了,如果被改变则抛出ConcurrentModificationException异常(fail-fast机制)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值