ArrayList源码

Posted 秋天de枫叶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList源码相关的知识,希望对你有一定的参考价值。

   ArrayList在编程中是比较常用的一个集合,ArrayList类是一个特殊的数组,通过添加和删除元素,就可以动态改变数组的长度。

public class ArrayList<E>extends AbstractList<E>
      implements List<E>, RandomAccess, Cloneable, Serializable

    ArrayList继承了AbstractList类,实现了List。即:具有了添加,删除,修改,遍历等功能。
    ArrayList实现了RandomAccess接口,即:能通过元素的序号快速访问对象。
    ArrayList实现了Cloneable接口,即:能被克隆。
    ArrayList实现了Serializable接口,即:支持序列化,能通过序列化传输。

// ArrayList带容量大小的构造函数。
    public ArrayList(int initialCapacity) 
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        // 新建一个数组
        this.elementData = new Object[initialCapacity];
    
    // ArrayList构造函数。默认容量是10。
    public ArrayList() 
        this(10);
    
    // 创建一个包含collection的ArrayList
    public ArrayList(Collection<? extends E> c) 
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    
 
	// 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2+1”
    public void ensureCapacity(int minCapacity) 
       // 将“修改统计数”+1
        modCount++;
        int oldCapacity = elementData.length;
      // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1”
        if (minCapacity > oldCapacity) 
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            elementData = Arrays.copyOf(elementData, newCapacity);
        
    

     通过构造函数可以看出,在使用ArrayList时,如果不设定初始容量的,ArrayList默认容量为10,因为ArrayList是动态数组,所以当容量不够时,ArryList会自动扩容的。自动扩容分两种情况,假设原来数组容量为10,并且已经有5个容量应经占用了,现在有要想数组中添加20 个元素。这时就需要考虑了,ArrayList扩容一次的容量为10x3/2+1=16,  16<20。说明扩容后的容量还是小于当前要添加的元素的个数,所以,ArrayList 直接将元素的个数当成现在要扩容的容量。也就是说,现在ArrayList的容量为30。
    所以ArrayList 扩容分两种情况:
    第一种:当要添加的元素个数,小于ArrayList扩容后容量时,Arraylist容量="(原始容量x3)/2+1"。
    第二种:当添加的元素个数,大于ArrayList扩容后的容量时,ArrayList容量=当前容量+元素的个数。

 ArrayList线程不安全,为什么说ArrayList线程不安全呢?

假设:
    在 Items[Size]的位置存放此元素;
    增大 Size 的值。
    在单线程运行的情况下,如果 Size= 0,添加一个元素后,此元素在位置 0,而且 Size=1;
    如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList添加元素,因为此时 Size 仍然等于 0(注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size的值。

        public static void main(String[] args) throws InterruptedException 
	        ThreadGroup group = new ThreadGroup("testgroup");
	        ArrayListInThread t = new ArrayListInThread();
	        for (int i = 0; i < 10000; i++) 
	            Thread th = new Thread(group, t, String.valueOf(i));
	            th.start();
	        
	       
	        while (group.activeCount() > 0) 
	            Thread.sleep(10);
	        
	        System.out.println();
            // it should be 10000 if thread safe collection is used.
	        System.out.println(t.list1.size()); 
	    
		
		public class ArrayListInThread implements Runnable 
		 
			List<String> list1 = new ArrayList<String>(1);// not thread safe
		 
			// List<String> list1 = Collections.synchronizedList(new
			// ArrayList<String>());// thread safe
			public void run() 
				try 
					Thread.sleep((int) (Math.random() * 2));
				 catch (InterruptedException e) 
					e.printStackTrace();
				
				list1.add(Thread.currentThread().getName());
		 
			
		 
		

总结
    1.ArrayList是一个线程不安全的集合
     2.ArrayList因为是一个有序的集合,通过元素序号可以快速访问。但是在增删时比较慢。因为需要移动元素位置。
    3.ArrayList是一个动态的数组,她的扩容方式分两种情况。
      第一种:当要添加的元素个数,小于ArrayList扩容后容量时,Arraylist容量="(原始容量x3)/2+1"。
      第二种:当添加的元素个数,大于ArrayList扩容后的容量时,ArrayList容量=当前容量+元素的个数。

 

以上是关于ArrayList源码的主要内容,如果未能解决你的问题,请参考以下文章

Java小白集合源码的学习系列:ArrayList

ArrayList 源码分析

List源码解析之ArrayList源码分析

源码阅读Java集合 - ArrayList深度源码解读

ArrayList源码简析

Java-ArrayList源码分析