日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

java中ArrayList与LinkedList的区别

發(fā)布時(shí)間:2025/4/17 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中ArrayList与LinkedList的区别 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、背景

面試題中經(jīng)常會(huì)被面試官問到ArrayList和LinkedList的區(qū)別,下面從源碼角度來對(duì)他們進(jìn)行一下簡(jiǎn)單的闡述,相信會(huì)對(duì)它們有一個(gè)更全面深入的了解。

首先,ArrayList和LinkedList都實(shí)現(xiàn)了List接口,ArrayList的底層是通過【動(dòng)態(tài)數(shù)組】實(shí)現(xiàn)的,LinkedList底層是通過【鏈表】實(shí)現(xiàn)的。

二、ArrayList

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

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

總結(jié)一下:通過add(e)方法在集合尾部添加數(shù)據(jù),效率還是比較高的,因?yàn)椴簧婕皵?shù)組元素的復(fù)制移動(dòng),但有時(shí)涉及到擴(kuò)容
2、通過get(index)根據(jù)索引獲取元素對(duì)象
下面是ArrayList類中的關(guān)于get(index)方法的代碼

public E get(int index) {// 校驗(yàn)索引index是否越界rangeCheck(index);// 調(diào)用elementData方法return elementData(index);}// 返回index下標(biāo)對(duì)應(yīng)的數(shù)組元素,不需要遍歷 E elementData(int index) {return (E) elementData[index];}

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

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

原理圖如下:

總結(jié)一下:add(index,e)方法涉及到元素的整體復(fù)制向后移動(dòng),元素下標(biāo)也會(huì)發(fā)生變化,此種方式添加元素效率較低,數(shù)組容量不足,也會(huì)進(jìn)行擴(kuò)容。

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

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

總結(jié)一下:remove(index)刪除元素,會(huì)導(dǎo)致剩下的元素整體復(fù)制移動(dòng),元素下標(biāo)會(huì)發(fā)生變化,因此該方式刪除元素?cái)?shù)據(jù)效率較低。

二、LinkedList

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

......省略......// 聲明容量sizetransient int size = 0;// 聲明首節(jié)點(diǎn)transient Node<E> first;// 聲明尾節(jié)點(diǎn)transient Node<E> last;// 無參構(gòu)造方法,執(zhí)行后size=0;first=null;last=nullpublic LinkedList() {}// add方法添加元素public boolean add(E e) {// 調(diào)用的核心方法linkLast(e);return true;}void linkLast(E e) {// 添加元素之前,先將當(dāng)前LinkedList對(duì)象的尾部節(jié)點(diǎn)賦給l對(duì)象final Node<E> l = last;// 創(chuàng)建一個(gè)新Node對(duì)象:上一個(gè)節(jié)點(diǎn)指向l,下一個(gè)節(jié)點(diǎn)指向null,元素E對(duì)象為efinal Node<E> newNode = new Node<>(l, e, null);// 將newNode新節(jié)點(diǎn)賦給last對(duì)象last = newNode;// 如果當(dāng)前LinkedList對(duì)象沒有尾節(jié)點(diǎn),即l==null,說明LinkedList對(duì)象中沒有節(jié)點(diǎn)元素。if (l == null)// 這種情況下,將新Node節(jié)點(diǎn)賦給首節(jié)點(diǎn)first = newNode;else// 如果LinkedList對(duì)象中已經(jīng)存在尾節(jié)點(diǎn),則將該尾節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)指向新添加的節(jié)點(diǎn)l.next = newNode;// size數(shù)加1size++;modCount++;}// 定義私有內(nèi)部靜態(tài)Node類:Node對(duì)象有三個(gè)屬性->元素E、指向上一個(gè)節(jié)點(diǎn)prev、指向下一個(gè)節(jié)點(diǎn)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對(duì)象結(jié)構(gòu)示意圖:

總結(jié)一下:LinkedList對(duì)象中通過add()方法添加元素時(shí),對(duì)已經(jīng)存在的元素沒有影響,沒有對(duì)其他元素的復(fù)制移動(dòng)等操作,效率高

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

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

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

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

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

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

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

public E get(int index) {// 檢查index索引合法性,是否越界。checkElementIndex(index);// 調(diào)用node方法return node(index).item;}/*** Returns the (non-null) Node at the specified element index.* 此方法根據(jù)傳入的index索引,進(jìn)行遍歷查詢得到相應(yīng)的元素*/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;}}

總結(jié)一下:LinkedList的get(index)方法通過遍歷查詢?cè)?#xff0c;效率比較低;而ArrayList中的get(index)直接通過下標(biāo)獲取數(shù)組元素,不用遍歷,效率更高。

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。