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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ArrayList 与 LinkedList 底层实现

發(fā)布時(shí)間:2024/9/30 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ArrayList 与 LinkedList 底层实现 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

ArrayList 與 LinkedList 都是List 類型的容器,保存數(shù)據(jù)的同時(shí)也維持著它們插入元素的順序,LinkedList 提供了更多的方法來(lái)操作其中的數(shù)據(jù),但是這并不意味著要優(yōu)先使用LinkedList ,因?yàn)檫@兩種容器底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)截然不同。

ArrayList

ArrayList 底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,因?yàn)閿?shù)組有下標(biāo)索引,所以在查詢與設(shè)置特定數(shù)據(jù)的時(shí)候會(huì)非???#xff0c;缺點(diǎn)就是在插入、刪除數(shù)據(jù)時(shí)效率很低(數(shù)組在是一塊連續(xù)的內(nèi)存空間,當(dāng)你想要?jiǎng)h除或者添加元素時(shí)都需要移動(dòng)內(nèi)存)。

下面就結(jié)合底層的源碼進(jìn)行分析:在查找元素時(shí)根據(jù)傳入的下標(biāo)就可以直接返回對(duì)應(yīng)的元素?cái)?shù)據(jù),但是在進(jìn)行元素刪除與添加時(shí)會(huì)根據(jù)下標(biāo)進(jìn)行對(duì)應(yīng)的元素拷貝,如果一個(gè)集合中有大量數(shù)據(jù),想要?jiǎng)h除一個(gè)特定位置上的元素,那么這個(gè)元素后的所有元素都會(huì)進(jìn)行拷貝,這個(gè)效率是相當(dāng)感人的,在添加元素時(shí)還會(huì)先對(duì)原來(lái)的數(shù)組長(zhǎng)度先進(jìn)行擴(kuò)容然后再進(jìn)行拷貝。

//其底層的數(shù)據(jù)結(jié)構(gòu)是一個(gè)Object 類型的數(shù)組transient Object[] elementData; // non-private to simplify nested class access//根據(jù)下標(biāo)返回在數(shù)組中對(duì)應(yīng)的數(shù)據(jù),在返回?cái)?shù)據(jù)之前回先調(diào)用rangeCheck(); 方法public E get(int index) {rangeCheck(index);return elementData(index); //當(dāng)傳入的下標(biāo)小于0 時(shí)報(bào)的異常會(huì)指向這一行代碼}//rangeCheck(); 方法用于對(duì)傳入的數(shù)組下標(biāo)進(jìn)行越界檢查,這里只檢查下標(biāo)超出數(shù)組長(zhǎng)度的情況private void rangeCheck(int index) {if (index >= size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));//當(dāng)傳入的下標(biāo)大于數(shù)組的長(zhǎng)度時(shí)報(bào)的異常會(huì)指向這一行代碼}//修改元素的值public E set(int index, E element) {rangeCheck(index); //對(duì)下標(biāo)范圍進(jìn)行檢查E oldValue = elementData(index);elementData[index] = element; //改變?cè)氐闹?/span>return oldValue;}//在指定的位置添加元素public void add(int index, E element) {rangeCheckForAdd(index); //先對(duì)下標(biāo)范圍檢查ensureCapacityInternal(size + 1); //對(duì)Object 數(shù)組進(jìn)行擴(kuò)容,這個(gè)擴(kuò)容步驟分很多步就不在這里貼出來(lái)了,感興趣的話可以自己查看源碼System.arraycopy(elementData, index, elementData, index + 1,size - index); //將數(shù)組中index 之后的元素向后拷貝elementData[index] = element; //將元素插入在固定的位置上size++;}//刪除元素的方法public E remove(int index) {rangeCheck(index); //先檢查刪除元素的下標(biāo)modCount++;E oldValue = elementData(index); //獲得指定下標(biāo)的元素int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved); //index 之后的元素全部向前拷貝elementData[--size] = null; // clear to let GC do its workreturn oldValue;}

LinkedList

LinkedList 底層是一個(gè)Node 類型的雙向鏈表,在當(dāng)前節(jié)點(diǎn)中不僅保存了元素值還保存了上一個(gè)元素和下一個(gè)元素的地址。

transient Node<E> first;transient Node<E> last; //內(nèi)部類,保存了當(dāng)前的元素至值以及上一個(gè)元素和下一個(gè)元素的地址private 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;}}

通過(guò)下面對(duì)LinkedList 的源碼分析: 我們發(fā)現(xiàn)想要獲得元素值會(huì)遍歷節(jié)點(diǎn),直到找到對(duì)應(yīng)節(jié)點(diǎn)的位置然后返回其中保存的元素值(修改操作類似)。對(duì)于刪除和插入的操作只要找到對(duì)應(yīng)的節(jié)點(diǎn)然后改變節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)的指向就可以完成任務(wù),不存在大量元素的拷貝,所以在對(duì)元素進(jìn)行刪除與插入時(shí)要比ArrayList 效率更高。

//獲取指定位置的元素值public E get(int index) {checkElementIndex(index); //對(duì)元素索引檢查return node(index).item; //返回對(duì)應(yīng)位置的節(jié)點(diǎn)值,下面這個(gè)方法是返回值算法的實(shí)現(xiàn)}Node<E> node(int index) {// assert isElementIndex(index);if (index < (size >> 1)) { //右移一位相當(dāng)于 size / 2,避免所有節(jié)點(diǎn)掃描Node<E> x = first;for (int i = 0; i < index; i++) //從頭結(jié)點(diǎn)開(kāi)始循環(huán)返回對(duì)應(yīng)位置節(jié)點(diǎn)上的元素值x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--) //從尾結(jié)點(diǎn)開(kāi)始循環(huán)返回對(duì)應(yīng)位置節(jié)點(diǎn)上的元素值x = x.prev;return x;}// 設(shè)置元素值public E set(int index, E element) {checkElementIndex(index); //檢查元素所以Node<E> x = node(index); //返回元素的節(jié)點(diǎn)E oldVal = x.item;x.item = element; //設(shè)置新的節(jié)點(diǎn)值return oldVal;}//在指定的位置添加元素public void add(int index, E element) {checkPositionIndex(index);if (index == size) //如果插入元素的位置等于集合的大小,那么直接在最后插入linkLast(element);elselinkBefore(element, node(index)); //先找到在index 位置上的元素節(jié)點(diǎn),然后進(jìn)行鏈接,對(duì)應(yīng)的方法如下}void linkBefore(E e, Node<E> succ) {// assert succ != null;final Node<E> pred = succ.prev; //獲得當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)final Node<E> newNode = new Node<>(pred, e, succ);succ.prev = newNode; //設(shè)置當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)為新插入的節(jié)點(diǎn)if (pred == null)first = newNode;elsepred.next = newNode; //將新插入的節(jié)點(diǎn)鏈接到當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)上size++;modCount++;}//刪除對(duì)應(yīng)位置上的元素public E remove(int index) {checkElementIndex(index);return unlink(node(index)); //調(diào)用unlink() 方法刪除節(jié)點(diǎn)}E unlink(Node<E> x) {// assert x != null;final E element = x.item; //獲得當(dāng)前節(jié)點(diǎn)的值以及前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)final Node<E> next = x.next;final Node<E> prev = x.prev;if (prev == null) {first = next;} else {prev.next = next; //如果不是頭結(jié)點(diǎn)就將當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)x.prev = null;}if (next == null) {last = prev;} else {next.prev = prev; //如果不是尾節(jié)點(diǎn)就將當(dāng)前節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)x.next = null;}x.item = null;size--;modCount++;return element;}

總結(jié)

ArrayList:內(nèi)部數(shù)據(jù)結(jié)構(gòu)是數(shù)組,查找與修改元素很快,增刪元素速度很慢–>地址空間連續(xù),還有一點(diǎn)要注意的是在刪除元素的時(shí)候從后面往前刪除元素要比從前往后刪除效率更高

LinkedList:內(nèi)部是雙向鏈表形式的數(shù)據(jù)結(jié)構(gòu),增刪元素的速度很快,但查找元素速度很慢,由于是雙向鏈表所以在對(duì)鏈表中間元素進(jìn)行操作時(shí)效率會(huì)很低

PS:在LinkedList 內(nèi)部,所有的增刪改查操作都會(huì)先找到對(duì)應(yīng)的節(jié)點(diǎn),所以在對(duì)LinkedList 中的元素進(jìn)行操作時(shí)效率都是相當(dāng)?shù)?。之所以說(shuō)它查詢速度慢是指與ArrayList 相比時(shí)它沒(méi)有數(shù)組獲取元素容易,刪除元素和添加元素快是因?yàn)橹灰业綄?duì)應(yīng)的節(jié)點(diǎn)然后對(duì)節(jié)點(diǎn)的指向進(jìn)行操作就可以將數(shù)據(jù)插入與刪除,在ArrayList 中,會(huì)將元素拷貝到新的數(shù)組中,拷貝的操作與LinkedList 節(jié)點(diǎn)操作相比并不高效。

System.arraycopy() 方法

因?yàn)锳rrayList 源碼中用到了這個(gè)方法,就在這里簡(jiǎn)單介紹一下

public static void (Object src, int srcPos, Object dest, int destPos, int length)
src:源數(shù)組;
srcPos:源數(shù)組要復(fù)制的起始位置;
dest:目的數(shù)組;
destPos:目的數(shù)組放置的起始位置;
length:復(fù)制的長(zhǎng)度。

舉個(gè)例子如下:如果這個(gè)方法復(fù)制的是數(shù)組對(duì)象,那么只是復(fù)制了對(duì)象的引用并不是對(duì)對(duì)象本身進(jìn)行復(fù)制,所以也被稱為淺復(fù)制。

int[] array = {0,1,2,3,4,5,6}; System.arraycopy(array, 0,array,3, 3);System.out.println(Arrays.toString(array)); //復(fù)制結(jié)果為:[0, 1, 2, 0, 1, 2, 6]

可參考文章: 模擬實(shí)現(xiàn)ArrayList與 LinkedList

與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

總結(jié)

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

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