ArrayList與linkedList面試事項
ArrayList 和linkedList 也是面試中經常遇到的問題,也是平時開發中最常用的list,瞭解這兩個的特性並恰當區分使用,可以一定程度上提高程式碼的執行效率。今個記一下這兩個的使用方法,並總結區別。
ArrayList
arraylist就是一個數組,一個動態陣列,可以動態改變元素,自定義陣列大小。實現了RandomAcess,Cloneable、Serializable介面。
一、先簡單看一下構造方法:
1.public ArrayList();
2.public ArrayList(ICollection);
3.public ArrayList(int);
第一種:預設的構造器,初始化一個容量為10的陣列。
第二種:傳入一個實現ICollection的物件,並將傳入物件中的元素賦值給新建的list;
第三種:自定義陣列的容量。
二、Arraylist中的方法
其中有get、set、add、indexof,contains,clone、size、toarray、isEmpty、sort等方法的使用,我覺得不是什麼難點,api介紹很清楚,在後續比較與linkedlist區別時會介紹一些,現在稍微總結一下:
1.當add方法一直新增,知道超過現有容量大小時,arraylist重新設定的容量為(原始容量*3)/2+1,也就是原本容量增加1/2
2.arraylist實現了Cloneable介面,中心就是其中的clone方法。可以將此物件的全部元素複製到另一個數組中。
3.arraylist實現Serializable介面,是可序列化的,當讀寫的時候,會容量先行,即:寫入的時候,先寫入容量,在放置元素;讀取時,先讀取容量,在去除元素。需要注意的是,可序列化只是一個標誌,意味著它可以被序列化,並沒有實際性的方法、欄位,但是這不代表沒有意義,因為在上述讀寫過程中,就會依次進行。我還沒有了解深入,但事實就是這樣。後續在補充吧。
三、ArrayList的遍歷
談到陣列,集合。免不了使用遍歷,
有三種方法可以遍歷ArrayList;
1.迭代器
部分程式碼:
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
2.隨機訪問
for (int i=0; i<list.size; i++) {
value = (Integer)list.get(i);
}
3.foreach
for (Integer integ:list) {
value = integ;
}
測試一下,就能知道他們之間的效率 隨機訪問>foreach>迭代器。這算是ArrayList的最大優點。
LinkedList
LinkedList是一個雙向連結串列,可以被當做堆疊、佇列或者雙向佇列。實現了list、Deque、Cloneable、Serializable介面。
一、為數不多的三個屬性:
1、size,記錄當前list有多少節點。
2、first,代表當前第一個節點。
3、last,代表當前最後一個節點。
後面的方法都與這仨屬性息息相關。
二方法
、構造方法:
1.public LinkedList() {
}
2.
public LinkedList(Collection<? extends E> c) { this(); addAll(c);
}
只有兩個,與arraylist中對應的兩個用法相同。
三、類中方法
1.add
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++; }此方法就是將新的元素放到連結串列最後,長度加1,修改次數加1;
2.帶參add
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }很清晰,不贅述。
3.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; } }首先判斷有沒有超出索引長度。然後和長度的1/2對比,小於就從頭遍歷,大於就從尾部遍歷。
(linkedlist不能快速訪問,只能挨個遍歷,為了儘可能少的遍歷,判斷從頭部開始還是尾部開始是個很好的方法)
4.remove
public E remove() { return removeFirst(); }
public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); }
private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }即:remove就是removefirst,將第一個節點設為null,並將全域性變數first設定為當前節點的下一個節點。長度減一,修改次數加一
5.removelast
private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; }將最後一個節點設為null,將原本最後一個節點的上一個節點設為全域性變數last,長度減一,修改次數加一。
6.remove(object o)
public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
從first到last迴圈遍歷,如果引數和當前元素相等,呼叫unlink(下面解釋此方法)
7.remove(int index)
public E remove(int index) { checkElementIndex(index); return unlink(node(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; } }先呼叫node方法,找到索引對應的值,之後和remove(object o)相同
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; }
這個方法的思路是獲取要移除節點的上一節點和下一節點,
如果上一節點是空,則list的first節點為當前節點的下一節點,
如果下一節點為空,list的last節點為當前節點的上一節點。
如果都不是,把當前節點設為null,這樣當前節點的上一節點指向當前節點的下一節點。
8.toarray
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
遍歷當前list,並挨個賦值到新的陣列中。
區別
從上面的分析已經可以大致看出兩者的區別了,總結一下:
linkedlist是基於連結串列的list,從大量的成員方法也能看出,新增,刪除佔了大比重,相比Arraylist的增刪,linkedlist更高效,因為沒有list容量的問題,不需要再新增時判斷容量是否超出;刪除時直接刪除元素,上一節點會指向下一節點,而arraylist刪除指定元素,後面的所有元素都必須向前移動相應位置。
ArrayList的快速訪問是一個優勢,從上面linkedlist的方法中可以看到,遍歷佔據了大量的份額,任何的查詢,大部分的刪除都得在真正的行為開始前進行所有元素的遍歷。而作為陣列的arrayList,可以通過索引,直接訪問到對應的元素。
總而言之,arraylist更適合讀取,linkedlist更適合增刪。