1.ArrayList实现原理

一.先看一下官方定义:

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.)

ArryList实际上是一个可动态增删得数组,因为是以数组为基础,所以它得get(i)/set(i,e) 的性能很高,直接在数组末尾加入元素—add(e)的性能也高,但是如果按下标插入、删除元素—add(i,e), remove(i), remove(e),则要用System.arraycopy()来移动部分受影响的元素,性能就变差了,这是基本劣势。ArrayList是线程不安全的。

二.字段及构造方法:

ArrayList继承了AbstractList,实现List,RandomAccess,Cloneable,Serializable。

它得字段主要有:

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData;

private int size;

transient关键字:

1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

它得构造方法主要有三个:

1.public ArrayList(int initialCapacity) ;//创建指定长度的ArrayList-参数大于0创建指定长度的ArrayList,等于0创建空的ArrayList,其他抛出异常。

 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

 

2.public ArrayList();//创建一个空的ArrayList

 

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

 

3.public ArrayList(Collection<? extends E> c);//接收一个集合

 

 

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

 

三.ArrayList主要方法:

 

1. public int indexOf(Object o) 查找目标数据并返回第一个所在位置,未找到则返回-1(ArrayList可以存储null)。

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

 

2.public boolean contains(Object o) 检查是否存在目标数据(其实内部实现就是调用indexOf()方法)。

 

 public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

3.public int lastIndexOf(Object o)  检查最后一个元素是否为目标数据。

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

4. public Object clone() 克隆当前list(此处为浅拷贝

public Object clone() {

        try {
            ArrayList<?> v = (ArrayList<?>) 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(e);
        }
    }

5.public Object[] toArray()   将list转变为数组

public Object[] toArray() { return Arrays.copyOf(elementData, size); }

6.public E get(int index) 根据下标获取数据

public E get(int index) {
        rangeCheck(index);//检查是否数据越界
        return elementData(index);
    }

7.public E set(int index, E element) 添加数据到指定位置 (其实就是对数组进行操作)

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

 8.public boolean add(E e)  向末尾添加数据

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

add操作详解:

1.首先确保内部容量

private void ensureCapacityInternal(int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断如果是创建的空集合且第一次添加数据,则最小容量设置为10.
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);//明确容量大小
    }

2.明确容量大小

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

3.扩充内部数组

 private void grow(int minCapacity) {
	        // overflow-conscious code
	        int oldCapacity = elementData.length;
	        int newCapacity = oldCapacity + (oldCapacity >> 1);//oldCapacity >> 1:oldCapacity/2.相当于扩充原来的1.5倍
	        if (newCapacity - minCapacity < 0)//检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量
	        	newCapacity = minCapacity; 
	        if (newCapacity - MAX_ARRAY_SIZE > 0) 
	        	newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: 
	        elementData = Arrays.copyOf(elementData, newCapacity);// 最后确定了新的容量,就使用Arrays.copyOf方法来生成新的数组,copyOf也已经完成了将旧的数据拷贝到新数组的工作 
	  }
	        // overflow-conscious code
	        int oldCapacity = elementData.length;
	        int newCapacity = oldCapacity + (oldCapacity >> 1);//oldCapacity >> 1:oldCapacity/2.相当于扩充原来的1.5倍
	        if (newCapacity - minCapacity < 0)//检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量
	        	newCapacity = minCapacity; 
	        if (newCapacity - MAX_ARRAY_SIZE > 0) 
	        	newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: 
	        elementData = Arrays.copyOf(elementData, newCapacity);// 最后确定了新的容量,就使用Arrays.copyOf方法来生成新的数组,copyOf也已经完成了将旧的数据拷贝到新数组的工作 
	  }
 

ArrayList的容量拓展,是创建一个新的数组,然后将旧数组上的数组copy到新数组,这是一个很大的消耗,所以在我们使用ArrayList时,最好能预计数据的大小,在第一次创建时就申请够内存

 

关于ArrayList默认大小的问题:刚创建出来的list其实长度是为0的,因为是空的List,当有第一次add()时才会将List的容器大小扩展为10(其实从List的构造方法也可以看出来)。

验证方式:

List<Long> list = new ArrayList<>();

list.set(0, 1L);

此方法会抛出异常:Exception in thread "main" java.lang.IndexOutOfBoundsException

 9. public void add(int index, E element) 向任意位置插入数据

public void add(int index, E element) {
        rangeCheckForAdd(index);//检查是否越界
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

其实就是将原数组从index位置开始到数组结束拷贝到另一个数组中,将index位置空出来,再将新数据插入进去

 

10. public E remove(int index)  删除指定位置数据

public E remove(int index) {
        rangeCheck(index);//检查是否越界
        modCount++;//操作+1
        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;
    }
public static void (Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)
src:源数组;	srcPos:源数组要复制的起始位置;
dest:目的数组;	destPos:目的数组放置的起始位置;	length:复制的长度。

后续:有时候我们用增强for循环,在中间添加或删除List中得成员时,会报ConcurrentModificationException。原因:因为ArrayList不是同步的,ArrayList里面有一个继承于AbstractList的成员变量modCount。该变量记录着对List的增加删除操作次数。而在使用增强for循环时,ArrayList的内部迭代器在进行迭代时会判断expectedModCount与modCount是否相等,初始化两个值是相等的。如果在迭代时对List进行增加删除操作,会导致两个数值不等。这也是报出异常的根本原因。

ArrayList大体内容就这些,其实还有一个主要的Iterator迭代器,自己慢慢摸索吧!

千里之行,始于足下!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值