其實也不像大家說的那回事,主要還是根據數據量和操作有關係!
我們先從add() 方法比較
ArrayList
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//指定下標添加元素
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_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);
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);
}
當ArrayList去add數據的時候
elementData[size++] = e; 先去把元素添加進去 ,因爲容量肯定是夠用的
oldCapacity + (oldCapacity >> 1) 這裏面則是用到了位運算進行擴容數組
elementData = Arrays.copyOf(elementData, newCapacity);
LikedList
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
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++;
}
對於add(int index,Element element)這個方法在中間添加數據
因爲ArrayList去添加數據 會移動數組或者擴容數組 !內部實現還是根據邏輯去擴容數組,是同數組具有下標的。
而LikedList在添加數據的時候只是new 一個Note對象並重新修改上一節點或下一個節點。達到目的
在內存方面ArrayList應該是佔的塊內存
我這面做的一個測試,如果add(0,element)的時候
getTime = System.currentTimeMillis();
for(int i = 0; i < 900;i++){
arrayList.add(0,i);
}
Log.e("time", "array" + (System.currentTimeMillis() - getTime));
getTime = System.currentTimeMillis();
for(int i = 0; i < 900;i++){
linkedList.add(0,i);
}
Log.e("time", "linked" + (System.currentTimeMillis() - getTime));
小數據的時候偶爾集合會較快,偶爾鏈表快,當數據量大的時候會更加明顯集合大於鏈表的添加時間。
我這面做的一個測試,如果add(size,element)的時候
小數據的時候偶爾集合會較快,大多數還是鏈表快,當數據量大的時候會更加明顯集合大於鏈表的添加時間。
我這面做的一個測試,如果add(2,element)的時候 ,也就是Index的時候 小數據的時候偶爾集合會較快,大多數還是鏈表快,大數據的話還是鏈表快的
remove對比
ArrayList
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(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; // clear to let GC do its work
return oldValue;
}
通過下標index 執行這段代碼
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
這面我舉個例子說明下
因爲原碼裏面都是elementData,也就是一個數組,
{0,1,2,3,4}
如果當remove的index爲1的時候
走完System.arraycopy()這個方法數組將會發生改變
{0,2,3,4,4}
然後 elementData[--size] = null;
size減減 保證arrayList.size()方法的準確拿取ArrayList的size大小
並把數組的最後一個數據設爲null
{0,2,3,4,null}
可見移除了下標爲1的數據
作爲
public static void arraycopy(
Object src, //源數組
int srcPos, //源數組的起始位置
Object dest, //目標數組
int destPos, //目標數組的起始位置
int length //複製長度
)
這個方法 當remove的時候內部實現會複製數組 index下標越小 複製的數組量越大,數組移動也就越多 耗時越大!
這面是這麼想的 但是當ArrayList add(int index)集合的時候,卻是index越小時間越短 和上面的分析完全相反!
這面我有點蒙,希望看到文章的評論解釋下~
LikedList
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
checkElementIndex(index); 檢查是否下標越界
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;
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
先解釋第一個方法, Node<E> node(int index)通過此方法其實也就是和二分法差不多 通過遍歷集合拿到此下標下的Note對象
然後在通過 E unlink(Node<E> x) 這一方法 重新關聯 prev 和 next 的Note對象 並把當前的Note對象
和當前Note對象的prev和next設置 null
所以相對於remove這一方法的總結 LikedList是相對快於ArrayList的
而對於get(int index)這個方法 可想而知肯定是ArrayList比較快的,因爲內部實現是數組是有下標的,而 LikedList 是通過和二分法差不多的查找Note對象。
最後總結下 :
add(0,element)
add(2,element)
add(size,element) ,
小數據確實ArrayList和 LikedList難以分辯誰快,因爲不是確定的
但是當數據大的話還是LikedList快於ArrayList的
remove(int index),LikedList快於ArrayList
get(int index),ArrayList快於LikedList
但是對於 public static void arraycopy(
Object src, //源數組
int srcPos, //源數組的起始位置
Object dest, //目標數組
int destPos, //目標數組的起始位置
int length //複製長度
)
這個方法還是存在困惑,對於ArrayList的add(index ,element)到底是index越大越費時還是index越小費時!