java集合之ArrayList解析

来源:互联网 发布:车载蓝牙软件下载 编辑:程序博客网 时间:2024/06/06 04:09

ArrayList是基于动态数组实现增删改查操作,进行顺序存储的,在内存中各个元素的地
址是连续的.

ArrayList组织结构图:

这里写图片描述

根据上面的结构图知道ArrayList继承AbstractList并实现了RandomAccess,Cloneable,Serializable这三个标记接口,所以AbstractList具有如下特性:
  • AbstractList:实现了增删改查的集合操作,并可以对集合进行迭代查询。
  • RandomAccess:随机访问性能很高.
  • Cloneable:ArrayList对象可以进行克隆.
  • Serializable:ArrayList对象可以进行序列化和反序列化存储和读取.
ArrayList简要基本用法:

public boolean add(E var1)//在集合末尾添加数据var1

public void add(int index, E var2)//在索引位置为index的位置处插入数据var2

public E remove(int index)//删除集合中索引位置为index的元素.

public boolean remove(Object var1)//删除集合中值为var1的元素,查找元素var1是用var1的equals方法比对的,如果var1是null,就查找集合中值为null的元素并删除.(这里需要注意的是删除的元素是集合中第一个为var1或null的元素,并不是所有).

public E set(int index, E var2)//修改索引为index元素的值为var2;

public E get(int index)//获取索引index处的元素数据.

ArrayList的遍历:

增强式for循环

ArrayList<Integer> arrayList=new ArrayList<>();        arrayList.add(1);        arrayList.add(2);        arrayList.add(3);        for (Integer integer:arrayList){            System.out.println(integer);        }

Iterator迭代

 ArrayList<Integer> arrayList=new ArrayList<>();        arrayList.add(1);        arrayList.add(2);        arrayList.add(3);        Iterator<Integer> iterator=arrayList.iterator();        while (iterator.hasNext()){            System.out.println(iterator.next());        }

ArrayList的元素过滤

Iterator迭代过滤

ArrayList<Integer> arrayList=new ArrayList<>();        arrayList.add(1);        arrayList.add(2);        arrayList.add(3);        Iterator<Integer> iterator=arrayList.iterator();        while (iterator.hasNext()){            Integer integer=iterator.next();            if(integer>=3){//删除ArrayList集合中大于等于3的所有元素                iterator.remove();            }        }

for循环过滤

抛出java.util.ConcurrentModificationException异常,所以这种方式是错误的,后面会介绍原因.
ArrayList<Integer> arrayList=new ArrayList<>();        arrayList.add(1);        arrayList.add(2);        arrayList.add(3);        for(Integer e:arrayList){            if(e>=3){                arrayList.remove(e);            }        }

ArrayList源码解析

成员变量说明:

private static final int DEFAULT_CAPACITY = 10默认存储空间为10

private static final Object[] EMPTY_ELEMENTDATA = new Object[0]空动态数组

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];空容量数组

transient Object[] elementData;动态获取存储地址的数组elementData,它是不可以进行序列化的,作用与static修饰符一样.

private int size;数组中有效元素的个数。

private static final int MAX_ARRAY_SIZE = 2147483639;//动态数组的最大容量.

构造函数说明:

默认构造函数,创建一个空容量的数组给elementData
public ArrayList() {        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;    }
创建指定容量的数组集合。
public ArrayList(int var1) {        if(var1 > 0) {//大于0创建指定长度的数组给elementData            this.elementData = new Object[var1];        } else {            if(var1 != 0) {                throw new IllegalArgumentException("Illegal Capacity: " + var1);//如果小于0抛出异常            }            this.elementData = EMPTY_ELEMENTDATA;//若等于0赋值空数组.        }    }
创建一个初始数据为var1的集合给elementData;
public ArrayList(Collection<? extends E> var1) {        this.elementData = var1.toArray();//首先调用集合的toArray转换为数组并赋值给elementData        if((this.size = this.elementData.length) != 0) {            if(this.elementData.getClass() != Object[].class) {                this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);            }        } else {            this.elementData = EMPTY_ELEMENTDATA;//长度为0,赋值空数组        }    }

公共方法解析:

add方法源码:

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

1.它首先调用ensureCapacityInternal确保数组元素的空间足够, ensureCapacityInternal源码如下:

private void ensureCapacityInternal(int var1) {        if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {            var1 = Math.max(10, var1);        }        this.ensureExplicitCapacity(var1);    }

2.判断数组是不是空的,如果是空的,则首次为它分配的容量至少为10,然后调用ensureExplicitCapacity.

private void ensureExplicitCapacity(int var1) {        ++this.modCount;        if(var1 - this.elementData.length > 0) {            this.grow(var1);        }    }

3.modCount表示内部修改次数(后面会解释),判断需要的长度是否大于当前数组elementData的长度,如果是就执行grow方法进行扩容.

private void grow(int var1) {        int var2 = this.elementData.length;        int var3 = var2 + (var2 >> 1);//右移一位相当于除2,所以是1.5倍        if(var3 - var1 < 0) {            var3 = var1;        }        if(var3 - 2147483639 > 0) {            var3 = hugeCapacity(var1);        }        this.elementData = Arrays.copyOf(this.elementData, var3);    }

4.首先将当前数组的长度扩容为1.5倍,如果此时扩容后的容量仍然小于需要的长度,就将当前数组的长度改为需要的长度.

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

这个方法与add(E var1)差不多,实现的思想是首先判断索引是否越界,如果越界抛出IndexOutOfBoundsException异常,否则执行确保容量足够的方法,其次将index索引后的所有元素向后移动, z在index索引处插入元素var1,最后修改size+1;

remove操作

public E remove(int var1) {        this.rangeCheck(var1);        ++this.modCount;        Object var2 = this.elementData(var1);        int var3 = this.size - var1 - 1;        if(var3 > 0) {            System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var3);        }        this.elementData[--this.size] = null;        return var2;    }

首先检查是否数组越界,如果没有越界执行modCount+1,修改modCound的值,然后获得数组elemntData中索引为var1的元素,计算index数组复制的便宜量,即将index+1处的所有元素向前移动,最后修改数组末尾的元素值为null,等待GC回收并返回删除的元素.

 public boolean remove(Object var1) {        int var2;        if(var1 == null) {            for(var2 = 0; var2 < this.size; ++var2) {                if(this.elementData[var2] == null) {                    this.fastRemove(var2);                    return true;                }            }        } else {            for(var2 = 0; var2 < this.size; ++var2) {                if(var1.equals(this.elementData[var2])) {                    this.fastRemove(var2);                    return true;                }            }        }        return false;    }    private void fastRemove(int var1) {        ++this.modCount;        int var2 = this.size - var1 - 1;        if(var2 > 0) {            System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2);        }        this.elementData[--this.size] = null;    }

remove(Object var1)删除数组元素需要区分null和非null值,非null值比较的对象是用equals.上面源码比较简单不再重复赘述.

清空ArrayList集合元素

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

实现思想是:迭代数组中的元素分别设置为null,修改size为0,modCount++;

ArrayList实现了Iterable接口

public interface Iterable<T> {    Iterator<T> iterator();}

子类需要实现iterator()方法并返回Iterator对象

public interface Iterator<E> {    boolean hasNext();    E next();    default void remove() {        throw new UnsupportedOperationException("remove");    }

调用ArrayList的iterator()方法调用下面的逻辑实现:

 public Iterator<E> iterator() {        return new ArrayList.Itr();    }    private class Itr implements Iterator<E> {        int cursor;        int lastRet;        int expectedModCount;        private Itr() {            this.lastRet = -1;            this.expectedModCount = ArrayList.this.modCount;        }        public boolean hasNext() {            return this.cursor != ArrayList.this.size;        }        public E next() {            this.checkForComodification();            int var1 = this.cursor;            if(var1 >= ArrayList.this.size) {                throw new NoSuchElementException();            } else {                Object[] var2 = ArrayList.this.elementData;                if(var1 >= var2.length) {                    throw new ConcurrentModificationException();                } else {                    this.cursor = var1 + 1;                    return var2[this.lastRet = var1];                }            }        }        public void remove() {            if(this.lastRet < 0) {                throw new IllegalStateException();            } else {                this.checkForComodification();                try {                    ArrayList.this.remove(this.lastRet);                    this.cursor = this.lastRet;                    this.lastRet = -1;                    this.expectedModCount = ArrayList.this.modCount;                } catch (IndexOutOfBoundsException var2) {                    throw new ConcurrentModificationException();                }            }        }

实际上得到的是内部类Itr的实例,注意上面执行next()方法每次迭代器操作的时候都要首先调用checkForComodification(),它是什么呢?

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

看到了吧它就是用来比对modCount与expectModCount的值,这里就知道了为什么对集合进行删除,添加的时候需要修改modCount的值,当二者不等的时候抛出异常.

iterator执行remove()方法时会执行this.expectedModCount = ArrayList.this.modCount;二者永远相等所以进行迭代执行过滤操作的时候不会出现异常。而for循环是发生了并发修改异常,为什么呢?迭代器内部会维护一些索引位置相关的数据,要求在迭代过程中,容器不能发生结构性变化,否则这些索引位置就失效了。所谓结构性变化就是添加、插入和删除元素,只是修改元素内容不算结构性变化。


ArrayList的特点:

  • 查询效率高
  • 删除和添加元素需要移动数组所以效率比较低
  • 与LinkList正好相反,后面会分析LinkList的实现源码。