ArrayList是List接口的可變數組的實現。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 接口外,此類還提供一些方法來操作內部用來存儲列表的數組的大小。
每個ArrayList實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它總是至少等於列表的大小(如果不指定capacity,默認是10)。
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this(10); }隨著向ArrayList中不斷添加元素,其容量也自動增長。自動增長會帶來數據向新數組的重新拷貝(影響性能),因此,如果可預知數據量的多少,可在構造ArrayList時指定其容量。在添加大量元素前,應用程序也可以使用ensureCapacity操作來增加ArrayList實例的容量,這可以減少遞增式再分配的數量。
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return true (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
/** * Increases the capacity of this ArrayList instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } }注意: 在ensureCapacity中,
Object oldData[] = elementData;是要在復制新數組前,保存原來數組的引用,因為後面這個引用會指向新的數組。但是保存後其實沒有用處。
elementData = Arrays.copyOf(elementData, newCapacity);注意,此實現不是同步的。如果多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步。
private transient Object[] elementData;
public ArrayList() { this(10); } public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } 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); }
此處也是用Arrays.copyOf實現數組的復制,重點研究一下。與生成新數組的時候一樣。
第一判斷ensureSize,如果夠直接插入,否則按照policy擴展,復制,重建數組。
第二步插入元素。
ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection extends E> c)、addAll(int index, Collection extends E> c)這些添加元素的方法。下面我們一一講解
1. set(int index, E element),取代,而非插入,返回被取代的元素
/** * Replaces the element at the specified position in this list with * the specified element. * * @param index index of the element to replace * @param element element to be stored at the specified position * @return the element previously at the specified position * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { RangeCheck(index); E oldValue = (E) elementData[index]; elementData[index] = element; return oldValue; }2.add(E e) 增加元素到末尾,如果size不溢出,自動增長
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return true (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }3.add(int index, E element) 增加元素到某個位置,該索引之後的元素都後移一位
/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { if (index > size || index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); ensureCapacity(size+1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }3.後面兩個方法都是把集合轉換為數組利用c.toArray,然後利用Arrays.copyOF 方法,重點研究一下
/** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the * specified collection's Iterator. The behavior of this operation is * undefined if the specified collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified collection is this list, and this * list is nonempty.) * * @param c collection containing elements to be added to this list * @return true if this list changed as a result of the call * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacity(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }Collection 接口定義了toArray方法,如下
/** * Returns an array containing all of the elements in this collection. * If this collection makes any guarantees as to what order its elements * are returned by its iterator, this method must return the elements in * the same order. * *下面是arraylist對其的一種實現:The returned array will be "safe" in that no references to it are * maintained by this collection. (In other words, this method must * allocate a new array even if this collection is backed by an array). * The caller is thus free to modify the returned array. * *
This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this collection */ Object[] toArray();
/** * Returns an array containing all of the elements in this list * in proper sequence (from first to last element). * *The returned array will be "safe" in that no references to it are * maintained by this list. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * *
This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this list in * proper sequence */ public Object[] toArray() { return Arrays.copyOf(elementData, size); }
一種是按索引刪除,不用查詢,索引之後的element順序左移一位,並將最後一個element設為null,由gc負責回收。
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; }
/** * Copies the specified array, truncating or padding with nulls (if necessary) * so the copy has the specified length. For all indices that are * valid in both the original array and the copy, the two arrays will * contain identical values. For any indices that are valid in the * copy but not the original, the copy will contain null. * Such indices will exist if and only if the specified length * is greater than that of the original array. * The resulting array is of the class newType. * * @param original the array to be copied * @param newLength the length of the copy to be returned * @param newType the class of the copy to be returned * @return a copy of the original array, truncated or padded with nulls * to obtain the specified length * @throws NegativeArraySizeException if newLength is negative * @throws NullPointerException if original is null * @throws ArrayStoreException if an element copied from * original is not of a runtime type that can be stored in * an array of class newType * @since 1.6 */ public static此處jdk有所優化,如果是object類型,直接new object數組,如果不是通過Array.newInstance調用native方法產生響應的數組類型,創建數組後,通過System.arrayCopy實現數組復制,System是個final類,copyof是個native方法。(如果實現,為何用native方法???)源碼如下T[] copyOf(U[] original, int newLength, Class extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
* @param src the source array. * @param srcPos starting position in the source array. * @param dest the destination array. * @param destPos starting position in the destination data. * @param length the number of array elements to be copied. * @exception IndexOutOfBoundsException if copying would cause * access of data outside array bounds. * @exception ArrayStoreException if an element in thesrc
* array could not be stored into thedest
array * because of a type mismatch. * @exception NullPointerException if eithersrc
or *dest
isnull
. */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
備注:
Native修飾符標示該方法的實現體非java,而是c++或者其他語言
2. 有助於提升性能
Java不是完美的,Java的不足除了體現在運行速度上要比傳統的C++慢許多之外,Java無法直接訪問到操作系統底層(如系統硬件等),為此Java使用native方法來擴展Java程序的功能。
可以將native方法比作Java程序同C程序的接口,其實現步驟:
1、在Java中聲明native()方法,然後編譯;
2、用javah產生一個.h文件;
3、寫一個.cpp文件實現native導出方法,其中需要包含第二步產生的.h文件(注意其中又包含了JDK帶的jni.h文件);
4、將第三步的.cpp文件編譯成動態鏈接庫文件;
5、在Java中用System.loadLibrary()方法加載第四步產生的動態鏈接庫文件,這個native()方法就可以在Java中被訪問了。
上述例子中,System.arrayCopy()和Array.newInsance(compnentType, length)均采用native方法,補充Array.newInstance()源碼如下:
* @param componentType theClass
object representing the * component type of the new array * @param length the length of the new array * @return the new array * @exception NullPointerException if the specified *componentType
parameter is null * @exception IllegalArgumentException if componentType is {@link Void#TYPE} * @exception NegativeArraySizeException if the specifiedlength
* is negative */ public static Object newInstance(Class> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length); }
private static native Object newArray(Class componentType, int length) throws NegativeArraySizeException;
利用Array.newInstance()創建數組的意義是什麼?
Java反射技術除了可以在運行時動態地決定要創建什麼類型的對象,訪問哪些成員變量,方法,還可以動態地創建各種不同類型,不同維度的數組。
動態創建數組的步驟如下:
1.創建Class對象,通過forName(String)方法指定數組元素的類型
2.調用Array.newInstance(Class, length_of_array)動態創建數組
訪問動態數組元素的方法和通常有所不同,它的格式如下所示,注意該方法返回的是一個Object對象
Array.get(arrayObject, index)
為動態數組元素賦值的方法也和通常的不同,它的格式如下所示, 注意最後的一個參數必須是Object類型
Array.set(arrayObject, index, object)
動態數組Array不單可以創建一維數組,還可以創建多維數組。步驟如下:
1.定義一個整形數組:例如int[] dims= new int{5, 10, 15};指定一個三維數組
2.調用Array.newInstance(Class, dims);創建指定維數的數組
訪問多維動態數組的方法和訪問一維數組的方式沒有什麼大的不同,只不過要分多次來獲取,每次取出的都是一個Object,直至最後一次,賦值也一樣。
動態數組Array可以轉化為普通的數組,例如:
Array arry = Array.newInstance(Integer.TYPE,5);
int arrayCast[] = (int[])array;
public static void main(String args[]) throws Exception { Class> classType = Class.forName("java.lang.String"); // 創建一個長度為10的字符串數組 Object array = Array.newInstance(classType, 10); // 把索引位置為5的元素設為"hello" Array.set(array, 5, "hello"); // 獲得索引位置為5的元素的值 String s = (String) Array.get(array, 5); System.out.println(s); }
public static void main(String args[]) { int[] dims = new int[] { 5, 10, 15 }; // 創建一個具有指定的組件類型和維度的新數組。 Object array = Array.newInstance(Integer.TYPE, dims); // 取出三維數組的第3行,為一個數組 Object arrayObj = Array.get(array, 3); Class> cls = arrayObj.getClass().getComponentType(); System.out.println(cls); // 取出第3行的第5列,為一個數組 arrayObj = Array.get(arrayObj, 5); // 訪問第3行第5列的第10個元素,為其賦值37 Array.setInt(arrayObj, 10, 37); // 動態數組和普通數組的轉換:強行轉換成對等的數組 int arrayCast[][][] = (int[][][]) array; System.out.println(arrayCast[3][5][10]); }
public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData;
/** * Save the state of the ArrayList instance to a stream (that * is, serialize it). * * @serialData The length of the array backing the ArrayList * instance is emitted (int), followed by all of its elements * (each an Object) in the proper order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; i
ArrayList是會開辟多余空間來保存數據的,而系列化和反序列化這些沒有存放數據的空間是要消耗更多資源的,所以ArrayList的數組就聲明為transient,自己實現write/readObject方法,僅僅系列化已經存放的數據
import java.io.*; public class Box implements Serializable //要保存的對象類必須實現序列化接口serializable { private int width; private int height; public void setWidth(int width){ this.width = width; } public void setHeight(int height){ this.height = height; } public static void main(String[] args){ Box myBox = new Box(); myBox.setWidth(50); myBox.setHeight(30); try{ //序列化。 FileOutputStream fs = new FileOutputStream("foo.ser"); ObjectOutputStream os = new ObjectOutputStream(fs); os.writeObject(myBox); os.close(); }catch(Exception ex){ ex.printStackTrace(); } } } //發序列化方法 Public static void seserialize(string filename) throws Exception{ // 反序列化(讀出保存的對象文件) ObjectInputStream in = new ObjectInputStream (new FileInputStream(filename)); Box box = (Box) (in.readbject()); System.out.println(box.toString()); In.Closer(); }為何要序列化,不是在內存中嗎ArrayList 實現了java.io.Serializable接口,在需要序列化的情況下,復寫writeObjcet和readObject方法提供適合自己的序列化方法。
1、序列化是干什麼的?
簡單說就是為了保存在內存中的各種對象的狀態(也就是實例變量,不是方法),並且可以把保存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保存object states,但是Java給你提供一種應該比你自己好的保存對象狀態的機制,那就是序列化。
2、什麼情況下需要序列化
a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想通過RMI傳輸對象的時候;
總結
1.Arraylist基於數組實現,是自增長的
2,非線程安全的
3.插入時可能要擴容,刪除時size不會減少,如果需要,可以使用trimToSize方法,在查詢時,遍歷查詢,為null,判斷是否是null, 返回; 如果不是null,用equals判斷,返回
/** * Returns true if this list contains the specified element. * More formally, returns true if and only if this list contains * at least one element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this list is to be tested * @return true if this list contains the specified element */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index i such that * (o==null ? get(i)==null : o.equals(get(i))), * or -1 if there is no such index. */ 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; }
4. 允許重復和 null 元素