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.
*
* 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();
下面是arraylist對其的一種實現: /**
* 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 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;
} 此處jdk有所優化,如果是object類型,直接new object數組,如果不是通過Array.newInstance調用native方法產生響應的數組類型,創建數組後,通過System.arrayCopy實現數組復制,System是個final類,copyof是個native方法。(如果實現,為何用native方法???)源碼如下* @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 the src
* array could not be stored into the dest array
* because of a type mismatch.
* @exception NullPointerException if either src or
* dest is null.
*/
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 theClassobject 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 *componentTypeparameter 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 元素