【Java分析】ArrayList 源码分析

目录:

一:java集合框架

二:ArrayList

三:LinkedList

四:HashMap

 

 

一:总结

0、继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。

1、可以存放null,可以重复(继承  list的原因)

2、可以随机访问 (继承  RandomAccess// get(index))

3、可以使用clone 因为继承  Cloneable接口

4、可以被序列化的   因为继承  Serializable

5、底层数据结构:数组

6、初始容量:10

7、扩容方式:1.5倍扩容,grow方法

8、求交集 并集 差集 addAll removeAll retainAll

9、ArrayList本质上就是A一个elementData数组。

10、增:尾插,随机插(先后移,然后插入)

        删:数组的拷贝来实现
11、因为本质是数组,所以查询快,而在插入删除慢。

12、和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。使用iterator遍历可能会引发多线程异常

13、如果我们明确所插入元素的多少,最好指定一个初始容量值, 避免过多的进行扩容操作而浪费时间、效率。

 

二:源码分析

1、定义:

当定义创建一个新对象时,默认数组大小为10即:DEFAULTCAPACITY_EMPTY_ELEMENTDATA

当传入容量大小为0时返回EMPTY_ELEMENTDATA的空数组

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //序列号
    private static final long serialVersionUID = 8683452581122892189L;
    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    //一个空数组,当指定ArrayList 容量为 0 时,返回该空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
//一个空数组实例,当没有指定 ArrayList 的容量时返回该数组,数据量为0   
//当用户第一次添加元素时,数组将扩容,变成默认容量为10的一个数组ensureCapacityInternal() 实现

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //保存数据,长度为容量大小
    transient Object[] elementData; // non-private to simplify nested class access
    //ArrayList实际存储的数据数量
    private int size;

    //数组缓冲区最大存储容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 

2、构造方法

方法有三:

//一:带有int的初始容量大小的构造方法
public ArrayList(int initialCapacity) {
        //初始容量大于0,创建指定大小对象数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        }
        //等于0 返回 之前定义的 EMPTY_ELEMENTDATA
        else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } 
        //小于0,抛出异常
        else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
//二:无参构造方法,返回之前定义的DEFAULTCAPACITY_EMPTY_ELEMENTDATA初始为10的数组
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//三:类型构造器
public ArrayList(Collection<? extends E> c) {
        //将集合转为数组
        elementData = c.toArray();
        //判断是否为空,为空则返回之前定义的 EMPTY_ELEMENTDATA,不为空将c长度赋值给当前 ArrayList 的 size
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            // 若 c.toArray() 返回的数组类型不是 Object[],则利用copyof方法创建一个Object[] 数组
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

3、方法

modeCount的说明:

*modCount 线程安全:其中保存了一个modCount属性 是为了线程安全。遍历的时候++ 使用的时候比较一下这俩一样不。

 

      (1)、添加:add  addAll

          add():

                尾插:为空初始化大小为10,否则1.5倍扩容

                指定位置插入:先判断插入位置是否越界然后,为空初始化大小为10,否则1.5倍扩容

                    尾插:

//尾插
public boolean add(E e) {
        //确定内部容量是否够用,size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1当前数组能否放下,放不下就扩容。
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //执行尾插
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal():判断当前数组是否为空,为空则初始化大小为10的数组

private void ensureCapacityInternal(int minCapacity) {
// 先判断当前数组是否是空的初始化数组,如果是空,就将minCapacity变成10,即初始化成长度为10的数组,也就是默认大小。
//这里就是判断如果为空则设置minCapacity为10,不为空进行下一步判断
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //判断当前数组是否够用
        ensureExplicitCapacity(minCapacity);
    }

 ensureExplicitCapacity():判断是否要扩容

private void ensureExplicitCapacity(int minCapacity) {
        
        modCount++;
        // 如果需要的长度  大于当前数组的长度就扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow()扩容方法  1.5倍扩容

private void grow(int minCapacity) {
        //当前数组大小赋值给oldCapacity
        int oldCapacity = elementData.length;
        // 扩容为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //这句话就是适应于elementData就空数组的时候,为空,则初始化大小为10
        if (newCapacity - minCapacity < 0)                   
            newCapacity = minCapacity;
        // 若 newCapacity 大于最大存储容量,就调用hugeCapacity,也就是将能给的最大值给newCapacity
        if (newCapacity - MAX_ARRAY_SIZE > 0)            
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //进行数组扩容 调用Arrays.copyOf方法,其实就是system.arrayCopy方法。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

 最大值赋值:

//赋最大值。
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();

//Integer.MAX_VALUE:2147483647   MAX_ARRAY_SIZE:2147483639  
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

      指定位置插入:

//指定位置插入
public void add(int index, E element) {
        //判断插入位置是否合法,否则抛出IndexOutOfBoundsException异常
        rangeCheckForAdd(index);
        //同上
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //重新拷贝数组,在index位置插入。
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

     

 

                addAll():循环调用add方法

 

public boolean addAll(Collection<? extends E> c) {
            return addAll(this.size, c);
        }

public boolean addAll(int index, Collection<? extends E> c) {
            //越界检查
            rangeCheckForAdd(index);
            int cSize = c.size();
            if (cSize==0)
                return false;

            checkForComodification();
            parent.addAll(parentOffset + index, c);
            this.modCount = parent.modCount;
            this.size += cSize;
            return true;
        }

由于ArrayList 是线程不安全的,该方法没有加锁,所以当一个线程正在将 c 中的元素加入 list 中,但同时有另一个线程在更改 c 中的元素,就会抛出ConcurrentModificationException(并发修改异常),modeCount就是记录了当前数组是否正在被修改。

parent.addAll:是其实就是AbstractLis抽象接口,也就是循环调用add方法。

public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e);
            modified = true;
        }
        return modified;
    }

                (2)、删除 

                       remove(int index):用后面的值覆盖从而删除

                       remove(Object):这个方法可以看出来,arrayList是可以存放null值得。

                       clear():每个元素置空

                       removeAll方法 (移除 list 中和 c 中共有的元素)

                       removeRange ( int fromIndex, int toIndex ) 方法 : 删除fromIndex到toIndex之间的全部元素左闭右开

                            1、remove:删除指定位置上的元素,覆盖删除

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);
            //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
            elementData[--size] = null; // clear to let GC do its work
            //返回删除的元素。
            return oldValue;
        }

                               2、remove(Object):这个方法可以看出来,arrayList是可以存放null值得。

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

                              3、 clear():每一个元素都置空

public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

                             4、removeAll方法 (移除 list 中和 c 中共有的元素)


    public boolean removeAll(Collection<?> c) {
        // 当 c == null,则抛出NPE
        Objects.requireNonNull(c); 
        //批量删除c             
        return batchRemove(c, false);
    }
    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 {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

                                         5、removeRange ( int fromIndex, int toIndex ) 方法 : 删除fromIndex到toIndex之间的全部元素


    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        //需要移动元素的个数
        int numMoved = size - toIndex;
        //进行元素拷贝后,需要删除的几个元素就复制到了最后几个位置
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                numMoved);
        // clear to let GC do its work
        // 删除后新的长度
        int newSize = size - (toIndex-fromIndex);
        // 将需要删除的元素(index在最后几个)置为 null
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

 

                    (3)、set()方法

public E set(int index, E element) {
        // 检验索引是否合法
        rangeCheck(index);
        // 旧值
        E oldValue = elementData(index);
        // 赋新值
        elementData[index] = element;
        // 返回旧值
        return oldValue;
    }

                   (4)、indexOf


    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;
        } 
        // 没有找到,返回-1
        return -1;
    }

三:遍历

        通过迭代器

查询遍历
     迭代器:Iterator<Integer> all = arrayList.iterator();
while(all.hasNext()){
    		    Integer value = all.next();
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值