java集合之ArrayList解析

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的实现源码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值