分析java.util.ArrayList的記憶體分配
阿新 • • 發佈:2019-01-06
看原始碼: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();
}
}