用pv操作描述如下前驅圖_LinkedList實現分析(二)——常用操作

上一篇文章LinkedList實現分析(一)——LinkedList初探與對象創建介紹了LinkedList中的一些重要屬性和構造方法,下面我們將詳細介紹一下LinkedList提高的常用方法的實現原理

元素添加

###add(E e)方法

往LinkedList添加元素,LinkedList提供了多重方式,首先介紹add方法,下面我們使用一個簡單的實例代碼來分析一下add方法的實現原理:

List myList = new LinkedList();//1myList.add("a"); //2myList.add("b"); //3myList.add("c"); //4

執行第一行代碼后創建了一個空的myList對象,此時myList如下圖所示:

e78a054de569d66e929cd4b7782d35f1.png

myList

然后執行第2行代碼myList.add("a"),把字符串"a"添加到myList中,通過debug方式進入add方法內部:

public boolean add(E e) { linkLast(e); return true;}

add又調用linkLast方法,linkLast方法實現如下:

void linkLast(E e) { final Node l = last; final Node newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++;}

在linkLast內部,此時e="a"。首先把last,此時myList是新創建的對象,last=null,賦值給一個臨時變量 l,這里的做法是用來保存last的值,然后用元素e創建一個新的Node對象,這里是往myList的尾部添加元素,因此創建的Node節點的構造方法最后一個參數是null,而第一個參數表示的是插入元素e的前一個元素指針,因此需要傳入l,因為在整個LinkedList中last屬性表示的最后一個元素。當創建好包含e的newNode節點的時候,此時newNode就應該是myList的最后一個節點,也是第一個節點(l==null),因此把newNode賦值給last和first,然后把表示myList大學的size進行加一,最后對modCount加一,記錄了修改次數。執行完第2行代碼后,myList的狀態入下圖所示:

298a7f22d5eb958097e5d8ee05041fc0.png

myList

當示例代碼執行第3行代碼的時,同樣會執行linkLast,此時e=“b”。在進入linkLast方法的時候,last的值已經不在是null,而是剛剛創建的包含了“a”的Node節點對象,同樣把last先用一個臨時變量l保存起來,然后創建一個包含“b”的Node節點,這個時候該node節點的前一個節點是“a”所在的節點,因此構造函數 new Node<>(l, e, null),把新創建的newNode作為整個myList的最后一個節點: last = newNode,而由于l不為null,也就是last不為null,所以需要把新創建的節點放到myList中最后一個節點的后面: l.next = newNode,最后修改size和modCount的值,完成“b”元素的添加。此時myList的狀態如下圖所示:

f8b357e119219b6d1b574ab614c6bfdb.png

然后示例代碼執行到第4行,此時myList已經有2個node,根據上圖可知last指向了“b”node,然創建“c”的node對象,讓“c”的prev執行“b”,然后myList中的last指向“c”,“b”的next執行“c”,執行完后myList的狀態如下圖所示:

eae581396c74dfa33b1f882ae29cfe30.png

### add(int index, E element)

該方法是在指定的索引的位置插入一個元素,index值指定的索引位置,element是需要插入的元素信息。這里還是以一個示例為基礎進行對 add(int index, E element) 源碼的分析,示例代碼如下:

List myList = new LinkedList();//1myList.add("a"); //2myList.add(0,"b")//3

當示例代碼執行完第2部的時候,myList的狀態如下圖所示:

248ab59935db696c1ac2ce5ef65b11c3.png

然后執行第3行代碼,具體 add(int index, E element) 的源碼如下:

public void add(int index, E element) { checkPositionIndex(index);//index校驗 if (index == size) linkLast(element); else linkBefore(element, node(index));}

首先是對傳入的index進行位置校驗,具體實現是要求index不能小于0,不能大于myList當前的大小。根據示例代碼,執行到第3行的時候,此時size=1,index=0,因此程序執行linkBefore方法,linkBefore是第一參數是新添加的元素element,第二個參數表示element需要被添加到myList中哪個元素的后面。這里傳入是位置索引,因此需要調用node(index)方法找到mylist中index對應的node對象,node方法實現如下:

Node node(int index) { if (index < (size >> 1)) { Node x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x;}

這里源碼中使用了一個技巧,通過判斷傳入的index與整個list的大小(size)的1/2進行比較,如果index=size/2,那么對myList進行倒序遍歷。這里需要大家注意的是源碼中使用右移操作獲取size的1/2,這個技巧希望大家可以掌握:

>在java中,數字左移表示乘2,數字右移除2

由于當前myList的size=1,右移之后是0,index=0,因此執行else的語句,從last指向的node開始倒序遍歷,此時myList只有一個index=0所指向的元素就是last的值,因此把last返回。node方法執行完之后,開始執行linkBefore方法:

void linkBefore(E e, Node succ) { final Node pred = succ.prev; final Node newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++;}

linkBefore方法就是一個雙向列表的插入操作,首先把succ的前驅節點prev保存到臨時變量pred,使用傳入的e(根據示例代碼,此時e=“b”)創建一個新的Node對象:newNode,由于是在succ節點的前面插入新元素"b",因此newNode的前驅點是之前succ節點的前驅節點,newNode的后節點就是succ節點本身:final Node newNode = new Node<>(pred, e, succ),此時myList的狀態如下圖所示:

0a61e693f3661dfa5992bc22cc324a78.png

然后執行 succ.prev = newNode,是把原來succ指向的前驅節點改成了newNode,因為newNode插入了succ節點前面,因此succ的prev就是newNode,指向完的myList狀態如下:

54ba3a3ad9d1758d0ecd6f050062f4d7.png

然后是判斷pred是否為null,在myList的當前狀態中, succ.prev是null,pred也就是null,所以把first指向newNode,也就是在myList的頭結點succ之前添加一個新元素newNode,即 first = newNode;然后對size和modCount加一,執行完最后myList的狀態如下圖:

3753c31e41e8bd3213673ad551abd02f.png

### addAll(Collection extends E> c)

下面介紹一下使用集合對象給LinkedList添加元素,addAll有兩個重載方法:

public boolean addAll(Collection extends E> c)public boolean addAll(int index, Collection extends E> c)

在源碼中,addAll(Collection extends E> c)實際是調用了addAll(int index, Collection extends E> c) :

public boolean addAll(Collection extends E> c) { return addAll(size, c);}

因此這里只介紹后面一個addAll的實現:

public boolean addAll(int index, Collection extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; //下面的代碼是查詢需要插入位置的前驅和后繼節點 Node pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } //把a中的元素創建為一個列表 //并且讓pred指向a的最后一個節點 for (Object o : a) { Node newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } //如果沒有后繼節點,那么c集合中最后一個節點就是 //整個list的最后節點 if (succ == null) { last = pred; } else { //如果在list找到插入的前驅和后繼節點 //把c中的最后一個節點的后繼節點指向succ //把后繼節點的前驅節點指向c中的最后一個節點 pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true;}

addAll方法內部,首先還是判斷index的值是否合法,然后對傳入的Collection對象c進行驗證, 接著在源碼中定義了兩個變量:

Node pred, succ;

pred表示需要插入c的前驅節點,succ表示c的后繼節點。當index=size表示是把c添加到LinkedList的尾部,因此執行succ=null和pred=last;否則調用 node(index)方法,找到后繼節點succ,然后把當前后繼節點的前驅節點賦值給pred。

找到所插入前驅和后繼節點之后,開始循環遍歷c,把c中的每一個元素創建一個Node對象,c中的前一個元素都是后一個元素的前驅節點,把c建立為一個鏈表。然后把c的鏈表插入的LinkedList中,具體見上面源碼的注釋,完成鏈表的連接操作只會,對size和modCount進行加1操作。

### 其他添加元素的方法

下面介紹一個往list頭插入元素的方法:

public void addFirst(E e) {linkFirst(e);}private void linkFirst(E e) { //保存第一個元素 final Node f = first; //頭插法,因此newNode的前驅節點是null,后繼節點是f final Node newNode = new Node<>(null, e, f); //讓新創建節點當整個list的頭節點 first = newNode; if (f == null) //如果f=null,表示當前list沒有一個元素,因此需要給last=newNode last = newNode; else //把之前的first接的前驅節點換成新創建的newNode f.prev = newNode; size++; modCount++; }

頭插入的原理就是在整個list中,找到一個元素first,把first的前驅節點換成新插入的元素,然后把新傳入元素的后繼節點指向之前的頭節點,具體實現見上面源碼和注釋

LinkedList提供了offer(E e),offerFirst(E e), offerLast(E e),addFirst(E e) ,addLast(E e),push(E e) 往list中添加一個元素的方法,這些方法的底層都是基于上面介紹的原理實現的:頭插和尾插。這里就不多做說明。

獲取元素

LinkedList獲取元素提供多種方法:

//根據索引獲取元素public E get(int index)//獲取頭元素public E peek()//獲取頭元素,并且把頭元素從list中刪除public E poll()//獲取頭元素,并且把頭元素從list中刪除public E pop()```下面追個介紹這些方法的具體實現:```public E get(int index) {checkElementIndex(index);return node(index).item;}

由上面源碼可知,get操作底層是調用的node方法,node方法參考上面的介紹。

下面是peek,peek操作是獲取頭元素,因此只需要獲取到LinkedList的first所執行的元素就可以了。

public E peek() {final Node f = first;return (f == null) ? null : f.item;}

下面介紹poll():

public E poll() { final Node f = first; return (f == null) ? null : unlinkFirst(f);}private E unlinkFirst(Node f) { //保存f的具體值 final E element = f.item; final Node next = f.next; f.item = null; f.next = null; // help GC //f的后繼節點賦值給LinkedList的first first = next; //next==null表示要刪除的節點是list的最后一個節點 if (next == null) last = null; else //表示刪除f之后的list列表中,頭結點的prev應該為null,而之前 //prev是執行f的 next.prev = null; size--; modCount++; return element; }

由上面源碼可知,當調用poll的時候,會把LinkedList的頭元素返回,然后把頭元素從LinkedList中刪除。

下面看一下pop方法:

public E removeFirst() {final Node f = first;if (f == null) throw new NoSuchElementException();return unlinkFirst(f);}

pop方法的底層也是調用unlinkFirst方法完成頭結點的刪除操作。

##To Array的操作

LinkedList提供了兩種方法完成list到數組的轉換:

public Object[] toArray()public  T[] toArray(T[] a)

第一個方法,是把list轉為Object類型的數組,具體是實現如下:

public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node x = first; x != null; x = x.next) result[i++] = x.item; return result; }

該方法的實現原理比較簡單,首先創建一個大小與list相同的object類型數組,然后從頭到尾遍歷list的元素,把每個元素放到創建的數組中,因為數組對象是Object類型,因此list中的任何元素都可以直接放入到數組中,最后把數組返回。

在T[] toArray(T[] a) 方法中,需要傳遞一個T類型的數組對象,這樣可以按照T類型返回T類型數組:

public  T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null; return a;}

首先是判斷傳入的數組a的大小是否大于或者等于list的大小,如果小于,那么利用反射機制重新創建一個數組,此時參數a和toArray的數組就不是同一個數組了,如果a的大小大于size的值,假設數組a={"1

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379121.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379121.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379121.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++多重繼承與虛基類及與.NET的比較

多重繼承前面我們介紹的派生類只有一個基類&#xff0c;稱為單基派生或單一繼承。在實際運用中&#xff0c;我們經常需要派生類同時具有多個基類&#xff0c;這種方法稱為多基派生或多重繼承。2.1 多重繼承的聲明&#xff1a;在 C 中&#xff0c;聲明具有兩個以上基類的派生類與…

Javascript的IE和Firefox兼容性匯編

window.event現有問題&#xff1a;使用 window.event 無法在 FF 上運行解決方法&#xff1a;FF 的 event 只能在事件發生的現場使用&#xff0c;此問題暫無法解決。可以這樣變通&#xff1a;原代碼(可在IE中運行)&#xff1a;<input type"button" name"someB…

Java Double類compareTo()方法與示例

雙類compareTo()方法 (Double class compareTo() method) compareTo() method is available in java.lang package. compareTo()方法在java.lang包中可用。 compareTo() method is used to check equality or inequality for this Double-object against the given Double-obje…

平院實訓門禁系統導入

這是我的配置&#xff08;如果是Win10最好每一步都管理員身份運行&#xff09; win7 SQLServer2008 VS2012 切記&#xff1a;注意&#xff1a;當你SQLserver創建數據庫和VS連接數據庫的時候得用同一種方式&#xff0c;要么都用window&#xff08;主機名&#xff09;&#xff0…

ffmpeg 解碼音頻(aac、mp3)輸出pcm文件

ffmpeg 解碼音頻&#xff08;aac、mp3&#xff09;輸出pcm文件 播放pcm可以參考&#xff1a; ffplay -ar 48000 -ac 2 -f f32le out.pcm main.c #include <stdio.h> #include <stdlib.h> #include <string.h>#include <libavutil/frame.h> #include …

Jquery getJSON()

getJSON與aspx 準備工作 Customer類 public class Customer{public int Unid { get; set; }public string CustomerName { get; set; }public string Memo { get; set; }public string Other { get; set; }}&#xff08;一&#xff09;ashx Customer customer new Customer { …

北京中信銀行總行地址_中信銀行拉薩分行舉行“存款保險標識”啟用和存款保險條例宣傳活動...

11月NOV中信銀行拉薩分行舉行“存款保險標識”啟用和《存款保險條例》宣傳活動揭牌啟用儀式111月Jul根據人民銀行和總行關于“存款保險標識”啟用工作相關要求&#xff0c;分行行領導高度重視“存款保險標識”啟用和《存款保險條例》宣傳活動工作&#xff0c;按照統一工作部署、…

Java ClassLoader getPackage()方法與示例

ClassLoader類的getPackage()方法 (ClassLoader Class getPackage() method) getPackage() method is available in java.lang package. getPackage()方法在java.lang包中可用。 getPackage() method is used to return the package that has been defined in ClassLoader or t…

C---編寫程序:求出1~1000之間能被7或12整除,但不能同時被二者整除的所有整數,將結果保存在數組中,要求程序數據的輸入、計算和輸出均使用函數實現。

編寫程序&#xff1a;求出1~1000之間能被7或12整除&#xff0c;但不能同時被二者整除的所有整數&#xff0c;將結果保存在數組中&#xff0c;要求程序數據的輸入、計算和輸出均使用函數實現。 編程思路&#xff1a;分別編寫函數input()、cal()、output()實現數據的輸入、計算和…

報告稱我國成最大移民輸出國 將形成投資產業鏈(關注)

時代特有的現象&#xff0c;我們應該予以關注 “現在國內房價這么高&#xff0c;政策也看不清&#xff0c;還不如逢高賣掉之前買的幾套房子&#xff0c;一兩套房子的錢辦個投資移民&#xff0c;趁還年輕&#xff0c;拿到綠卡后享受一下美國本國待遇的高等教育了。”廣州&#x…

轉整型_156.Ruby烘焙大理石豆沙吐司解鎖大理石花紋整型

好看又好吃的大理石豆沙面包。紅豆餡均勻分布在松軟細膩的面包體里&#xff0c;手撕著吃&#xff0c;一層層的甜美與溫柔&#xff5e;關于吐司面包&#xff0c;我公眾號里寫過白吐司(基礎款牛奶吐司&#xff0c;超綿鮮奶油吐司)和全麥吐司(基礎款50%全麥吐司&#xff0c;經典燕…

ffmpeg 解碼視頻(h264、mpeg2)輸出yuv420p文件

ffmpeg 解碼視頻&#xff08;h264、mpeg2&#xff09;輸出yuv420p文件 播放yuv可以參考&#xff1a;ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25 out.yuv main.c #include <stdio.h> #include <stdlib.h> #include <string.h>#includ…

VS2010 快捷鍵 (空格顯示 綠點, Tab 顯示箭頭)

VS2010 有用的快捷鍵 &#xff1a; Ctrl r, ctrl w, 切換空格示。 轉載于:https://www.cnblogs.com/fengye87626/archive/2012/11/21/2780716.html

C---編寫程序:實現一個隨堂測試,能進行加減乘除運算。要求如下:(1)隨機產生兩個1~10的正整數,在屏幕上輸出題目,如:5+3=?(2)學生輸入答案,程序檢查學生輸入答案是否正確,若正確,

編寫程序&#xff1a;實現一個隨堂測試&#xff0c;能進行加減乘除運算。要求如下&#xff1a; 1&#xff09;隨機產生兩個1~10的正整數&#xff0c;在屏幕上輸出題目&#xff0c;如&#xff1a;53&#xff1f; 2&#xff09;學生輸入答案&#xff0c;程序檢查學生輸入答案是…

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 這兩個box信息

分析一下mp4格式的trak -> mdia -> minf -> stbl -> stts、stsc 這兩個box信息 &#xff08;因為這兩個box在音頻trak和視頻trak 下都有的&#xff0c;而且都有一個數組的值是比較繞的&#xff09; 目錄&#xff1a;stts&#xff1a;記錄時間戳的&#xff0c;每個s…

利用SQL注入2分鐘入侵網站

說起流光、溯雪、亂刀&#xff0c;可以說是大名鼎鼎無人不知無人不曉&#xff0c;這些都是小榕哥的作品。每次一提起小榕哥來&#xff0c;我的崇拜景仰就如滔滔江水&#xff0c;連綿不絕~~~~&#xff08;又來了&#xff01;&#xff09; 讓我們崇拜的小榕哥最新又發布了SQL注入…

pip安裝deb_技術|如何在 Ubuntu 上安裝 pip

pip 是一個命令行工具&#xff0c;允許你安裝 Python 編寫的軟件包。 學習如何在 Ubuntu 上安裝 pip 以及如何使用它來安裝 Python 應用程序。有許多方法可以在 Ubuntu 上安裝軟件。 你可以從軟件中心安裝應用程序&#xff0c;也可以從下載的 DEB 文件、PPA(LCTT 譯注&#xff…

assubclass_Java類class asSubclass()方法及示例

assubclass類類asSubclass()方法 (Class class asSubclass() method) asSubclass() method is available in java.lang package. asSubclass()方法在java.lang包中可用。 asSubclass() method casts this Class object to denote a subclass of the class denoted by the given…

VB6.0 怎樣啟用控件comdlg32.ocx

VB6.0 怎樣啟用控件comdlg32.ocx 怎樣啟用控件comdlg32.ocx 2008-10-08 09:32 提問者&#xff1a; nefu_20061617 |瀏覽次數&#xff1a;1502次vbs文件中有代碼Set ComDlg CreateObject("MSComdlg.CommonDialog")運行時發生錯誤ActiveX 部件不能創建對象: MSComdlg.…

Python---爬蟲案例

例1、爬取公眾號文章中的圖片。 1&#xff0c;首先打開要獲取公眾號文章的地址 2&#xff0c;按下F12&#xff0c;再按Ctrl Shift C&#xff0c;然后鼠標移動到圖片位置&#xff0c;然后觀察控制臺中顯示圖片對應的代碼位置 3&#xff0c;分析該位置的代碼段 代碼段如下&…