日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java中ArrayList与LinkedList的区别

發布時間:2025/4/17 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中ArrayList与LinkedList的区别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、背景

面試題中經常會被面試官問到ArrayList和LinkedList的區別,下面從源碼角度來對他們進行一下簡單的闡述,相信會對它們有一個更全面深入的了解。

首先,ArrayList和LinkedList都實現了List接口,ArrayList的底層是通過【動態數組】實現的,LinkedList底層是通過【鏈表】實現的。

二、ArrayList

1、通過add(e)方法添加元素
java中的數組一旦定義之后長度length就不可變了,是不可變數組;而python是可變數組,這點需要注意這兩種語言的不同;ArrayList可以不斷的通過add添加元素,它的size也是變化的,數組的長度又是不可變的,而ArrayList的底層是數組,它們不就矛盾了嗎?別急,ArrayList是通過判斷當前集合中元素的size數與數組的長度比較,如果size>數組length,再對數組擴容,然后將元素賦值給擴容后的數組
下面是截取的ArrayList類中的關于add方法的代碼

private static final int DEFAULT_CAPACITY = 10; //定義一個int常量,值為10 private static final Object[] EMPTY_ELEMENTDATA = {}; //定義一個空數組常量,值為{} private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定義一個默認數組常量,值為{} transient Object[] elementData; //定義一個數組 private int size; //定義size/** * 無參構造方法,new一個ArrayList對象后,實際上也創建并初始化了一個elementData的數組,且 * 這個數組為空數組{},length長度為0 */ public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }/** * 執行add(e)方法添加元素,可以看到先調用ensureCapacityInternal方法,再對 * elementData數組進行賦值。 */ public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true; }/** * ensureCapacityInternal方法傳入的minCapacity形參對應的實參值=size+1,它下面再去調用其他 * 方法,我們一層層深入,抽絲剝繭到后面的方法 */ private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }// calculateCapacity方法根據傳入的數組是否為空和minCapacity參數來確認并返回int private static int calculateCapacity(Object[] elementData, int minCapacity) {// arrayList第一次添加元素e時,size=0,minCapacity=1,調用此代碼,return返回10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}// arrayList 第二次及以后添加元素e,會直接執行此處代碼,并return (size+1)>=2return minCapacity; }/** * 上面calculateCapacity方法返回值作為實參傳遞給下面的ensureExplicitCapacity方法的形參并進* 行判斷處理。 */ private void ensureExplicitCapacity(int minCapacity) {modCount++;/*** 第1種情況:第一次添加元素e時,minCapacity=10,數組長度length=0* 第2種情況:第二次及以后添加e,minCapacity=size+1>=2* minCapacity與數組elementData長度進行比較,如果前者大于數組長度,則進行數組擴容,執行* grow方法。* 當屬于第1種情況時,數組長度=0,我們無法添加元素到數組中,所以需要執行grow()方法擴容,擴容* 的本質就是執行Arrays.copyOf()方法,得到長度為10的數組,然后再給數組賦值;* 當屬于第2種情況時,數組長度已經進行了第一次擴容,length=10,當添加第二個元素e時,* minCapacity=2 < elementData.length=10,也就是說數組容量足夠,可以直接添加元素,不必* 擴容*/if (minCapacity - elementData.length > 0)grow(minCapacity); }// 下面的grow方法就是擴容的核心代碼 private void grow(int minCapacity) {// overflow-conscious codeint 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); }

總結一下:通過add(e)方法在集合尾部添加數據,效率還是比較高的,因為不涉及數組元素的復制移動,但有時涉及到擴容
2、通過get(index)根據索引獲取元素對象
下面是ArrayList類中的關于get(index)方法的代碼

public E get(int index) {// 校驗索引index是否越界rangeCheck(index);// 調用elementData方法return elementData(index);}// 返回index下標對應的數組元素,不需要遍歷 E elementData(int index) {return (E) elementData[index];}

總結一下:get(index)不需要遍歷,直接取出相應下標的數組元素,效率較高。
3、通過add(index,e)指定位置添加元素
下面是ArrayList類中的關于add(index,e)方法的源代碼

public void add(int index, E element) {// 檢驗index有效性rangeCheckForAdd(index);// 根據傳參size+1,判斷是否擴容ensureCapacityInternal(size + 1); // Increments modCount!!// 數組復制,指定index對應的位置空出System.arraycopy(elementData, index, elementData, index + 1,size - index);// 數組index下標賦值插入的數據elementData[index] = element;size++;}

原理圖如下:

總結一下:add(index,e)方法涉及到元素的整體復制向后移動,元素下標也會發生變化,此種方式添加元素效率較低,數組容量不足,也會進行擴容。

4、remove(index)刪除元素源碼

public E remove(int index) {// 檢查下標的有效性rangeCheck(index);modCount++;// 獲取被刪除元素value值E oldValue = elementData(index);// 被刪元素后面需要被移動的元素個數int numMoved = size - index - 1;if (numMoved > 0)// 數組復制并向前移動1位System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}

總結一下:remove(index)刪除元素,會導致剩下的元素整體復制移動,元素下標會發生變化,因此該方式刪除元素數據效率較低。

二、LinkedList

1、通過add(e)方法添加元素
以下是LinkedList類中部分源碼。

......省略......// 聲明容量sizetransient int size = 0;// 聲明首節點transient Node<E> first;// 聲明尾節點transient Node<E> last;// 無參構造方法,執行后size=0;first=null;last=nullpublic LinkedList() {}// add方法添加元素public boolean add(E e) {// 調用的核心方法linkLast(e);return true;}void linkLast(E e) {// 添加元素之前,先將當前LinkedList對象的尾部節點賦給l對象final Node<E> l = last;// 創建一個新Node對象:上一個節點指向l,下一個節點指向null,元素E對象為efinal Node<E> newNode = new Node<>(l, e, null);// 將newNode新節點賦給last對象last = newNode;// 如果當前LinkedList對象沒有尾節點,即l==null,說明LinkedList對象中沒有節點元素。if (l == null)// 這種情況下,將新Node節點賦給首節點first = newNode;else// 如果LinkedList對象中已經存在尾節點,則將該尾節點的下一個節點指向新添加的節點l.next = newNode;// size數加1size++;modCount++;}// 定義私有內部靜態Node類:Node對象有三個屬性->元素E、指向上一個節點prev、指向下一個節點nextprivate static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}

LinkedList對象結構示意圖:

總結一下:LinkedList對象中通過add()方法添加元素時,對已經存在的元素沒有影響,沒有對其他元素的復制移動等操作,效率高

2、通過add(index,e)添加元素到指定位置
以下是相關的源碼,關鍵代碼做了注釋。

public void add(int index, E element) {// 校驗index是否越界checkPositionIndex(index);// index == size時,直接在最后添加,方法同上add(e)if (index == size)linkLast(element);else// 在集合首尾之間指定index處添加元素場景下的核心方法linkBefore(element, node(index));}// add(index,e)的底層核心方法void linkBefore(E e, Node<E> succ) {// 獲取指定index處Node對象的上一個節點對象predfinal Node<E> pred = succ.prev;// new一個新節點對象,指定它的上一個節點對象是pred,下一個節點對象是succfinal Node<E> newNode = new Node<>(pred, e, succ);// 重新設置指定index索引Node對象的上一個節點對象為newNodesucc.prev = newNode;// 判斷指定index處Node對象的上一個節點對象pred是否為空if (pred == null)// 如果為空,則指定newNode節點為首節點first = newNode;else// 如果不為空,則將指定indexNode對象的上一個節點對象的pred的下一個節點對象設置為newNodepred.next = newNode;size++;modCount++;}// 該方法返回指定index處的Node節點對象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;}}

總結一下:add(index,e)方法添加元素到集合中的指定位置,只是改變了上一個節點和下一個節點的指向位置,其他元素不受影響,所以比ArrayList的add(index,e)的效率要高,但需注意在查找index節點時,進行了遍歷,如果size比較大的話,遍歷會比較耗時。

3、通過remove(index)的方法刪除元素。
以下是LinkedList中相關源碼,關鍵代碼做了注釋

public E remove(int index) {// 檢查index索引是否合法checkElementIndex(index);// 調用node和unlink方法return unlink(node(index));}/*** Returns the (non-null) Node at the specified element index.* 返回指定index索引處的Node<E>對象*/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) {// 獲取index處Node對象的元素efinal E element = x.item;// 獲取index處Node對象的下一個節點Node對象nextfinal Node<E> next = x.next;// 獲取index處Node對象的上一個節點Node對象prevfinal Node<E> prev = x.prev;// 若index節點指向的上一個節點為null,說明index是首節點,刪除之后,將next節點賦值給首節點if (prev == null) {first = next;} else {// 若index節點指向的上一個節點prev不為null,說明被刪節點不是首節點,此時將prev的下一個節點指向next節點prev.next = next;// index節點指向的上一個節點賦null值,代表取消它的指向關系。x.prev = null;}// 若index節點指向的下一個節點next為null,說明被刪節點是尾節點,將prev節點賦值給尾節點if (next == null) {last = prev;} else {// 若next不為null,說明被刪節點不是尾節點,此時將next節點的上一個節點指向prev節點next.prev = prev;// index節點指向的下一個節點賦值為null值,代表取消它的指向關系x.next = null;}// index節點對應的元素Element賦值為null,結合上面的x.prev=null/x.next=null,代表該節點已經被刪除了。x.item = null;size--;modCount++;return element;}

總結一下:remove(index)方法刪除集合中的元素只是改變了上一個節點和下一個節點的指向位置,對其他元素沒有造成影響,效率比較高,但需注意在查找index節點時,進行了遍歷,如果size比較大的話,遍歷會比較耗時

4、通過get(index)獲取集合中的元素
以下是LinkedList中的部分源碼,關鍵部分做了注釋。

public E get(int index) {// 檢查index索引合法性,是否越界。checkElementIndex(index);// 調用node方法return node(index).item;}/*** Returns the (non-null) Node at the specified element index.* 此方法根據傳入的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;}}

總結一下:LinkedList的get(index)方法通過遍歷查詢元素,效率比較低;而ArrayList中的get(index)直接通過下標獲取數組元素,不用遍歷,效率更高。

總結

以上是生活随笔為你收集整理的java中ArrayList与LinkedList的区别的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。