ArrayList
在Android中的大紅人,經常用到,但是我卻沒有仔細考慮過,為啥直接就new 一個ArrayList,為啥不用LinkedList,什麼情況下用?所以對ArrayList分析一下,學習一下。
程式碼分析:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
複製程式碼
- 繼承AbstractList<E>類
AbstractList 繼承自 AbstractCollection 抽象類,實現了 List 介面,實現了一些介面如 get,set,remove,且實現了迭代器Iterator,簡化我們要實現一個資料結構的操作,比如實現一個順序表Array,或實現一個連結串列LinkedList等。
- 實現List<E>介面
List 一個元素有序,可重複,可為null的集合,實現List介面的類,元素通過索引進行排序
- 實現RandomAccess介面
一個標記介面,支援快速隨機訪問
- 實現Cloneable介面
支援clone
- 實現Serizllizable介面
序列化,物件的資訊可以通過網路傳輸,可以持久化寫入儲存區
屬性
//預設大小
private static final int DEFAULT_CAPACITY = 10;
//陣列的實現方式 順序表實現
transient Object[] elementData;
private int size;
複製程式碼
構造方法
//可以傳入容量大小
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);
}
複製程式碼
add(E e)
新增一個元素
public boolean add(E e) {
//檢查容量
ensureCapacityInternal(size + 1);
// Increments modCount!!
//末尾新增
elementData[size++] = e;
return true;
}
複製程式碼
- 首先檢查陣列大小容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
//如果當前是一個空的集合,就使用預設的容量大小
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//當前的容量已經無法繼續新增元素,擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //當前容量
int newCapacity = oldCapacity + (oldCapacity >> 1);//預設擴容當前容量的1/2
if (newCapacity - minCapacity < 0)
//如果擴容大小還不足以容納,採用傳進的大小進行擴容
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//判斷是否超出了最大了容量限制
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//將原來的資料copy到新的陣列,並完成擴容操作
elementData = Arrays.copyOf(elementData, newCapacity);
}
複製程式碼
- 新增元素預設是在末尾新增
add(int index, E element)
指定位置插入指定的元素
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
/**
*引數1:資料來源的陣列
*引數2:copy的起始位置
*引數3:新的接收陣列
*引數4:新的將要賦值的起始位置
*引數5:copy的長度
/
//將index後面的資料後移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//將空出的index位置賦值element
elementData[index] = element;
//容量加一
size++;
}
複製程式碼
從上面可以看出,在中間位置插入一個元素,時間複雜度為O(n)
get(int index)
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//直接取出陣列index位置的元素
return (E) elementData[index];
}
複製程式碼
可以看出,Arraylist讀取元素是比較快的O(1)
remove(int index)
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//記錄資料結構改變的次數
modCount++;
//獲取index位置的元素
E oldValue = (E) elementData[index];
//刪除index的元素,其它元素移動的位數
int numMoved = size - index - 1;
//將index後面的元素前移numMoved位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
複製程式碼
##其它常見方法
//獲取元素位置的索引
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;
}
//獲取最後一個o的索引
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
複製程式碼
從上面的簡單原始碼分析可以看到,ArrayList底層是以個陣列實現的順序表,在插入,修改,刪除的過程中效率不高,O(n) ;在查詢的時候效率較高,讀取速度快O(1),在Android 手機端,我們常見的listview中對讀取的速度要求較高,所以常採用Arraylist.
Other
順序表
將表中元素一個接一個的存入一組連續的儲存單元中,這種儲存結構是順序結構。
transient
java語言的關鍵字,變數修飾符,如果用transient宣告一個例項變數,當物件儲存時,它的值不需要維持。換句話來說就是,用transient關鍵字標記的成員變數不參與序列化過程。