1. 程式人生 > >分析java.util.ArrayList的記憶體分配

分析java.util.ArrayList的記憶體分配

看原始碼:java.util.ArrayList.class檔案,進行分析:

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;//預設的初始化陣列長度預設是10

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};//空陣列,當呼叫無引數建構函式的時候預設給個空陣列.

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;// 真正用來存元素的陣列

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;//ArrayList集合的大小/元素的數量.


//無參構造器:預設給個空陣列
//呼叫如 ArrayList a = new ArrayList(); ArrayList<String> strA = new ArrayList<String>();
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

//帶引數(初始大小)的構造器:initialCapacity 指定陣列長度
//呼叫如 ArrayList b = new ArrayList(3); ArrayList<String> strB = new ArrayList<String>(6);
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

//帶物件引數的構造器:將Collection裡面的值copy到arrayList
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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);
    }


//---------------新增操作的流程Start:--------------------------------
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e; // 將物件e放入elementData陣列中下標為size的位置, size自加1.
        return true;
    }


    //傳入引數 minCapacity = size + 1;//可理解為 將要放入 陣列位置的下標
    private void ensureCapacityInternal(int minCapacity) {
    //如果用於存元素的陣列elementData等於空(呼叫ArrayList無參構造器時 預設給的空陣列),則要初始化它
        if (elementData == EMPTY_ELEMENTDATA) {
    //用Math的max函式比較得出(預設值10,minCapacity)誰更大,取最大值傳入ensureExplicitCapacity方法。
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

// 當ArrayList裡的elementData空間不夠放時,則呼叫grow方法增大空間;
// 存前9個時 if都是false、 不增大, 存第十個時 minCapacity=11 、if為true、增大;  
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //基類的變數,初始值0.

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//2147483639
    Integer的MAX_VALUE:public static final int   MAX_VALUE = 0x7fffffff;

//
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // ">>"是"帶符號的右移";
        // "n >> 1"相當於 "n除以2";
        // newCapacity = oldCapacity乘以1.5。

//再判斷一下新陣列的容量夠不夠,夠了就直接使用minCapacity這個長度建立新陣列,
//不夠就將陣列長度設定為需要的長度
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity; // 無參構造的list物件第一次add時 走這裡.

        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);//新new一個[newCapacity]Object陣列,將原陣列內容拷貝過去.
    }

//----------新增操作的流程- END ----------------------------
    
    
    //修整elementData陣列的長度,一般是當這個List集合裝載完畢 不需要再裝入東西時 呼叫一下,以節省資源.
    /**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = Arrays.copyOf(elementData, size);
        }
    }


使用無參建構函式分配的儲存資料的陣列長度是10,
每接近一次elementData.length,進行按照當前陣列長度的1.5倍進行分配記憶體大小,並將資料從一個數組拷貝到新陣列。

所以,呼叫ArrayTest()構造器,存放元素時候,預設的陣列長度變化:10;15;22;

為了不浪費記憶體,如果能預先估計需要儲存到ArrayList的資料量,推薦使用public ArrayList(int initialCapacity)進行初始化。

或者也可以用ArrayList的trimToSize方法減去浪費的記憶體(但是重新排一遍也會耽誤一定的效能)。

下面,我們可以自定義一個ArrayList:定義一個ArrayTest類

package com.mylist;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;

/**
 * 自定義的ArrayTest類
 * @ProjectName:TestProject201609
 * @FileName: ArrayTest.java
 * @Description:ArrayTest
 * @version 0.1
 * @author Ally
 * @date 2016年09月28日
 */
public class ArrayTest<E>  extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
	
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;//預設的初始化陣列長度預設是10

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};//空陣列,當呼叫無引數建構函式的時候預設給個空陣列.

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;// 真正用來存元素的陣列

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;//ArrayList集合的大小/元素的數量.


//無參構造器:預設給個空陣列(length=0)
//呼叫如 ArrayList a = new ArrayList(); ArrayList<String> strA = new ArrayList<String>();
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayTest() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
        System.out.println("--- 呼叫 構造器- ArrayTest();"+" elementData長度="+elementData.length);
    }

//帶引數(初始大小)的構造器:initialCapacity 指定陣列長度
//呼叫如 ArrayList b = new ArrayList(3); ArrayList<String> strB = new ArrayList<String>(6);
    public ArrayTest(int initialCapacity) {
        super();
        System.out.println("--- 呼叫 構造器- ArrayTest(int initialCapacity)");
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }


//帶物件引數的構造器:將Collection裡面的值copy到arrayList
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayTest(Collection<? extends E> c) {
    	System.out.println("--- 呼叫 構造器- ArrayTest(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);
    }


//---------------新增操作的流程Start:--------------------------------
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
    	System.out.println("--ArrayTest新增元素 Start--》 size="+size +"; elementData長度="+elementData.length);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e; // 將物件e放入elementData陣列中下標為size的位置, size自加1.
        System.out.println("--ArrayTest新增元素 END--》 size="+size +"; elementData長度="+elementData.length);
        return true;
    }


    //傳入引數 minCapacity = size + 1;//可理解為 將要放入 陣列位置的下標
    private void ensureCapacityInternal(int minCapacity) {
    	System.out.println("ensureCapacityInternal方法》》傳入的minCapacity="+minCapacity);
    	//如果用於存元素的陣列elementData等於空(呼叫ArrayList無參構造器時 預設給的空陣列),則要初始化它
        if (elementData == EMPTY_ELEMENTDATA) {
        	System.out.println("elementData == EMPTY_ELEMENTDATA");
        	//用Math的max函式比較得出(預設值10,minCapacity)誰更大,取最大值傳入ensureExplicitCapacity方法。
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

	// 當ArrayList裡的elementData空間不夠放時,則呼叫grow方法增大空間;
	// 存前9個時 if都是false、 不增大, 存第十個時 minCapacity=11 、if為true、增大; 
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //基類的變數,初始值0.

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//2147483639
//    Integer的MAX_VALUE:public static final int   MAX_VALUE = 0x7fffffff;

	// 
    private void grow(int minCapacity) {
    	System.out.println("進入grow方法:");
        // overflow-conscious code
        int oldCapacity = elementData.length;
        /**
			">>"是"帶符號的右移";
			"n >> 1"相當於 "n除以2";
			newCapacity = oldCapacity乘以1.5。
         */
        int newCapacity = oldCapacity + (oldCapacity >> 1);

		/**
		再判斷一下新陣列的容量夠不夠,夠了就直接使用minCapacity這個長度建立新陣列, 
		不夠就將陣列長度設定為需要的長度
		 */
        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);//新new一個[newCapacity]Object陣列,將原陣列內容拷貝過去.
//        	elementData = copyOf(elementData, newCapacity);
        System.out.println("oldCapacity="+oldCapacity);
        System.out.println("newCapacity="+newCapacity);
        System.out.println("grow方法結束. elementData長度="+elementData.length);
    }
    
//-------------------新增操作的流程- END -------------------------------------------------------
    
    
    //修整elementData陣列的長度,一般是當這個List集合裝載完畢 不需要再裝入東西時 呼叫一下,以節省資源.
    /**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     */
    public void trimToSize() {
        modCount++;
        System.out.println("=== trimToSize呼叫前:size="+size + "; elementData陣列長度="+elementData.length);
        if (size < elementData.length) {
            elementData = Arrays.copyOf(elementData, size);
        }
        System.out.println("=== trimToSize呼叫結束:size="+size + "; elementData陣列長度="+elementData.length);
    }


	@Override
	public int size() {
		return size;
	}


	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return false;
	}


	@Override
	public boolean contains(Object o) {
		// TODO Auto-generated method stub
		return false;
	}


	@Override
	public Iterator<E> iterator() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object[] toArray() {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public <T> T[] toArray(T[] a) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public boolean remove(Object o) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean containsAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean addAll(int index, Collection<? extends E> c) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean removeAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}




	@Override
	public boolean retainAll(Collection<?> c) {
		// TODO Auto-generated method stub
		return false;
	}




	@Override
	public void clear() {
		// TODO Auto-generated method stub
		
	}




	@Override
	public E get(int index) {
		// TODO Auto-generated method stub
		return null;
	}




	@Override
	public E set(int index, E element) {
		// TODO Auto-generated method stub
		return null;
	}




	@Override
	public void add(int index, E element) {
		// TODO Auto-generated method stub
		
	}




	@Override
	public E remove(int index) {
		// TODO Auto-generated method stub
		return null;
	}




	@Override
	public int indexOf(Object o) {
		// TODO Auto-generated method stub
		return 0;
	}




	@Override
	public int lastIndexOf(Object o) {
		// TODO Auto-generated method stub
		return 0;
	}




	@Override
	public ListIterator<E> listIterator() {
		// TODO Auto-generated method stub
		return null;
	}




	@Override
	public ListIterator<E> listIterator(int index) {
		// TODO Auto-generated method stub
		return null;
	}




	@Override
	public List<E> subList(int fromIndex, int toIndex) {
		// TODO Auto-generated method stub
		return null;
	}

    
    
	
}


測試一下效果:

package com.mylist;
/**
 * @ProjectName:TestProject201609
 * @FileName: AmyT.java
 * @Description:AmyT
 * @version 0.1
 * @author AllyLai
 * @date 2016年09月28日
 */
public class AmyT {
	
	public static void main(String args[]) {  
//		int oldCapacity = 10;
//		int newCapacity = oldCapacity + (oldCapacity >> 1);
//		System.out.println(newCapacity);//15
		
		
		//走ArrayTest()構造器:
		ArrayTest a = new ArrayTest();
//		ArrayTest<String> a = new ArrayTest<String>();
		
		
		//走ArrayTest(int initialCapacity)構造器:
//		ArrayTest a = new ArrayTest(6);
//		ArrayTest<String> a = new ArrayTest<String>(6);
		
		a.add("1號");
		a.add("2號");
		a.add("3號");
		a.add("4號");
		a.add("5號");
		a.add("6號");
		a.add("7號");
		a.add("8號");
		a.add("9號");
		a.add("10號");
		a.add("11號");
		a.add("12號");
		a.add("13號");
		a.add("14號");
		a.add("15號");
		a.add("16號");
		a.add("17號");
		a.add("18號");
		a.add("19號");
		a.add("20號");
		a.add("21號");
		a.add("22號");
		a.add("23號");
		
		a.trimToSize();
		
	}

}