前言
android 開發(fā)適配器里面存放數(shù)據(jù)用的最多的就是ArrayList,大家有看過ArrayList源碼嗎靶溜,ArrayList的優(yōu)點(diǎn)和確定是什么开瞭,有沒有什么數(shù)據(jù)結(jié)構(gòu)可以替換ArrayList呢?本篇文章帶領(lǐng)大家從源碼角度出發(fā)一起看看ArrayList和LinkedList罩息。
1. ArrayList
ArrayList本質(zhì)是數(shù)組嗤详。它里面的元素都保存在elementdata[] 這樣一個數(shù)組里面
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!! 給ArrayList擴(kuò)容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
add的核心代碼是System.arraycopy(elementData, index, elementData, index + 1, size - index);
private static void arraycopy(char[] src, int srcPos, char[] dst, int dstPos, int length) {
if (src == null) {
throw new NullPointerException("src == null");
}
if (dst == null) {
throw new NullPointerException("dst == null");
}
if (srcPos < 0 || dstPos < 0 || length < 0 ||
srcPos > src.length - length || dstPos > dst.length - length) {
throw new ArrayIndexOutOfBoundsException(
"src.length=" + src.length + " srcPos=" + srcPos +
" dst.length=" + dst.length + " dstPos=" + dstPos + " length=" + length);
}
if (length <= ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD) {
// Copy char by char for shorter arrays.
if (src == dst && srcPos < dstPos && dstPos < srcPos + length) {
// Copy backward (to avoid overwriting elements before
// they are copied in case of an overlap on the same
// array.)
for (int i = length - 1; i >= 0; --i) {
dst[dstPos + i] = src[srcPos + i];
}
} else {
// Copy forward.
for (int i = 0; i < length; ++i) {
dst[dstPos + i] = src[srcPos + i];
}
}
} else {
// Call the native version for longer arrays.
arraycopyCharUnchecked(src, srcPos, dst, dstPos, length);
}
}
為了方便大家了解,我畫了一個圖
所以copy的操作元素就是遍歷元素扣汪,然后進(jìn)行替換操作断楷。
同理,remove()方法也是用了System.copy()方法崭别,只不過是把元素往前挪位置了
掌握了ArrayList方法的原理后我們不難發(fā)現(xiàn)冬筒,如果我們要往ArrayList中添加元素的時候,如果是往最后一個位置添加元素的時候速度是最快的茅主,往中間添加元素舞痰,因為涉及到元素位移操作,所以效率相對較低诀姚。由于arraylist本質(zhì)是數(shù)組响牛,數(shù)組在內(nèi)存中是連續(xù)的空間,所以get(index)是很快的赫段,直接通過elementdata[index]這個數(shù)組就能拿到呀打。
從時間復(fù)雜度來說,ArrayList插入和刪除的時候時間復(fù)雜度為O(n)糯笙,獲取元素的時間復(fù)雜度為O(1)贬丛。
那么有沒有插入和刪除相對較快的數(shù)據(jù)結(jié)構(gòu)呢。
2. LinkedList
LinkedList是一個雙鏈表给涕。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
就是把之前的鏈斷開豺憔,前面的元素的尾結(jié)點(diǎn)指向新添加的元素额获,新添加的元素的頭結(jié)點(diǎn)指向前一個元素。后一個元素的頭結(jié)點(diǎn)指向當(dāng)前元素恭应,當(dāng)前元素的尾結(jié)點(diǎn)指向后一個元素抄邀。
同樣的畫個圖方便大家了解
<meta charset="utf-8">
接下來看下get()方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
...
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
get()方法最終用for()循環(huán)是輪詢獲取元素,所以效率較低昼榛。
從時間復(fù)雜度來說境肾,LinkedList插入和刪除的時候時間復(fù)雜度為O(1),獲取元素的時間復(fù)雜度為O(n)褒纲。
有沒有一種數(shù)據(jù)結(jié)構(gòu)能夠結(jié)合以上兩個的優(yōu)點(diǎn)呢准夷,下篇文章咱們一起學(xué)習(xí)下HashMap的原理。