电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

Java集合 LinkedList的原理及使用詳解

瀏覽:7日期:2022-08-21 10:25:44

LinkedList和ArrayList一樣是集合List的實現(xiàn)類,雖然較之ArrayList,其使用場景并不多,但同樣有用到的時候,那么接下來,我們來認(rèn)識一下它。

一. 定義一個LinkedList

public static void main(String[] args) { List<String> stringList = new LinkedList<>(); List<String> tempList = new ArrayList<>(); tempList.add('牛魔王'); tempList.add('蛟魔王'); tempList.add('鵬魔王'); tempList.add('獅駝王'); tempList.add('獼猴王'); tempList.add('禺賊王'); tempList.add('美猴王'); List<String> stringList2 = new LinkedList<>(tempList);}

上面代碼中采用了兩種方式來定義LinkedList,可以定義一個空集合,也可以傳遞已有的集合,將其轉(zhuǎn)化為LinkedList。我們看一下源碼

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{ transient int size = 0; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node<E> last; /** * Constructs an empty list. */ public LinkedList() { } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection’s * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public LinkedList(Collection<? extends E> c) { this(); addAll(c); }}

LinkedList繼承了AbstractSequentialList類,實現(xiàn)了List接口,AbstractSequentialList中已經(jīng)實現(xiàn)了很多方法,如get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index),這些方法是我們集合操作時使用最多的,不過這些方法在LinkedList中都已經(jīng)被重寫了,而抽象方法在LinkedList中有了具體實現(xiàn)。因此我們回到LinkedList類

LinkedList類中定義了三個變量

size:集合的長度 first:雙向鏈表頭部節(jié)點 last:雙向鏈表尾部節(jié)點

針對first變量和last變量,我們看到是Node類的實體,這是一個靜態(tài)內(nèi)部類,關(guān)于靜態(tài)內(nèi)部類的講解,我們在static五大應(yīng)用場景一章已經(jīng)有說明

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; }}

我們知道LinkedList是通過雙向鏈表實現(xiàn)的,而雙向鏈表就是通過Node類來體現(xiàn)的,類中通過item變量保存了當(dāng)前節(jié)點的值,通過next變量指向下一個節(jié)點,通過prev變量指向上一個節(jié)點。

二. LinkedList常用方法

1. get(int index)

我們知道隨機(jī)讀取元素不是LinkedList所擅長的,讀取效率比起ArrayList也低得多,那么我來看一下為什么

public E get(int index) { checkElementIndex(index); return node(index).item;}/** * 返回一個指定索引的非空節(jié)點. */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; }}

從上述代碼中我們可以看到get(int index)方法是通過node(int index)來實現(xiàn)的,它的實現(xiàn)機(jī)制是:

比較傳入的索引參數(shù)index與集合長度size/2,如果是index小,那么從第一個順序循環(huán),直到找到為止;如果index大,那么從最后一個倒序循環(huán),直到找到為止。也就是說越靠近中間的元素,調(diào)用get(int index方法遍歷的次數(shù)越多,效率也就越低,而且隨著集合的越來越大,get(int index)執(zhí)行性能也會指數(shù)級降低。因此在使用LinkedList的時候,我們不建議使用這種方式讀取數(shù)據(jù),可以使用getFirst(),getLast()方法,將直接用到類中的first和last變量。

2. add(E e) 和 add(int index, E element)

大家都在說LinkedList插入、刪除操作效率比較高,以stringList.add(“豬八戒”)為例來看到底發(fā)生了什么?

在LinkedList中我們找到add(E e)方法的源碼

public boolean add(E e) { linkLast(e); return true;}/** * 設(shè)置元素e為最后一個元素*/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:假如stringList為空,那么添加進(jìn)來的node就是first,也是last,這個node的prev和next都為null;

Java集合 LinkedList的原理及使用詳解

情況2:假如stringList不為空,那么添加進(jìn)來的node就是last,node的prev指向以前的最后一個元素,node的next為null;同時以前的最后一個元素的next.

Java集合 LinkedList的原理及使用詳解

而如果通過stringList.add(1, “豬八戒”)這種方式將元素添加到集合中呢?

//在指定位置添加一個元素public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index));}/** * 在一個非空節(jié)點前插入一個元素 */void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++;}

其實從代碼中看到和add(E e)的代碼實現(xiàn)沒有本質(zhì)區(qū)別,都是通過新建一個Node實體,同時指定其prev和next來實現(xiàn),不同點在于需要調(diào)用node(int index)通過傳入的index來定位到要插入的位置,這個也是比較耗時的,參考上面的get(int index)方法。

其實看到這里,大家也都明白了。

LinkedList插入效率高是相對的,因為它省去了ArrayList插入數(shù)據(jù)可能的數(shù)組擴(kuò)容和數(shù)據(jù)元素移動時所造成的開銷,但數(shù)據(jù)擴(kuò)容和數(shù)據(jù)元素移動卻并不是時時刻刻都在發(fā)生的。

3. remove(Object o) 和 remove(int index)

這里removeFirst()和removeLast()就不多說了,會用到類中定義的first和last變量,非常簡單,我們看一下remove(Object o) 和 remove(int index)源碼

//刪除某個對象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;}//刪除某個位置的元素public E remove(int index) { checkElementIndex(index); return unlink(node(index));}//刪除某節(jié)點,并將該節(jié)點的上一個節(jié)點(如果有)和下一個節(jié)點(如果有)關(guān)聯(lián)起來E unlink(Node<E> x) { 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;}

其實實現(xiàn)都非常簡單,先找到要刪除的節(jié)點,remove(Object o)方法遍歷整個集合,通過 == 或 equals方法進(jìn)行判斷;remove(int index)通過node(index)方法。

Java集合 LinkedList的原理及使用詳解

4. LinkedList遍歷

我們主要列舉一下三種常用的遍歷方式,

普通for循環(huán),增強(qiáng)for循環(huán),Iterator迭代器

public static void main(String[] args) { LinkedList<Integer> list = getLinkedList(); //通過快速隨機(jī)訪問遍歷LinkedList listByNormalFor(list); //通過增強(qiáng)for循環(huán)遍歷LinkedList listByStrengThenFor(list); //通過快迭代器遍歷LinkedList listByIterator(list);}/** * 構(gòu)建一個LinkedList集合,包含元素50000個 * @return */private static LinkedList<Integer> getLinkedList() { LinkedList list = new LinkedList(); for (int i = 0; i < 50000; i++){ list.add(i); } return list;}/** * 通過快速隨機(jī)訪問遍歷LinkedList */private static void listByNormalFor(LinkedList<Integer> list) { // 記錄開始時間 long start = System.currentTimeMillis(); int size = list.size(); for (int i = 0; i < size; i++) { list.get(i); } // 記錄用時 long interval = System.currentTimeMillis() - start; System.out.println('listByNormalFor:' + interval + ' ms');}/** * 通過增強(qiáng)for循環(huán)遍歷LinkedList * @param list */public static void listByStrengThenFor(LinkedList<Integer> list){ // 記錄開始時間 long start = System.currentTimeMillis(); for (Integer i : list) { } // 記錄用時 long interval = System.currentTimeMillis() - start; System.out.println('listByStrengThenFor:' + interval + ' ms');}/** * 通過快迭代器遍歷LinkedList */private static void listByIterator(LinkedList<Integer> list) { // 記錄開始時間 long start = System.currentTimeMillis(); for(Iterator iter = list.iterator(); iter.hasNext();) { iter.next(); } // 記錄用時 long interval = System.currentTimeMillis() - start; System.out.println('listByIterator:' + interval + ' ms');}

執(zhí)行結(jié)果如下:

listByNormalFor:1067 mslistByStrengThenFor:3 mslistByIterator:2 ms

通過普通for循環(huán)隨機(jī)訪問的方式執(zhí)行時間遠(yuǎn)遠(yuǎn)大于迭代器訪問方式,這個我們可以理解,在前面的get(int index)方法中已經(jīng)有過說明,那么為什么增強(qiáng)for循環(huán)能做到迭代器遍歷差不多的效率?

通過反編譯工具后得到如下代碼

public static void listByStrengThenFor(LinkedList<Integer> list) { long start = System.currentTimeMillis(); Integer localInteger; for (Iterator localIterator = list.iterator(); localIterator.hasNext(); localInteger = (Integer)localIterator.next()) {} long interval = System.currentTimeMillis() - start; System.out.println('listByStrengThenFor:' + interval + ' ms');}

很明顯了,增強(qiáng)for循環(huán)遍歷時也調(diào)用了迭代器Iterator,不過多了一個賦值的過程。

還有類似于pollFirst(),pollLast()取值后刪除的方法也能達(dá)到部分的遍歷效果。

三. 總結(jié)

本文基于java8從定義一個LinkList入手,逐步展開,從源碼角度分析LinkedList雙向鏈表的結(jié)構(gòu)是如何構(gòu)建的,同時針對其常用方法進(jìn)行分析,包括get,add,remove以及常用的遍歷方法,并簡單的說明了它的插入、刪除操作為何相對高效,而取值操作性能相對較低,若有不對之處,請批評指正,望共同進(jìn)步,謝謝!

到此這篇關(guān)于Java集合 LinkedList的原理及使用詳解的文章就介紹到這了,更多相關(guān)Java LinkedList內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 森旺-A级防火板_石英纤维板_不燃抗菌板装饰板_医疗板 | 广州展台特装搭建商|特装展位设计搭建|展会特装搭建|特装展台制作设计|展览特装公司 | 环氧铁红防锈漆_环氧漆_无溶剂环氧涂料_环氧防腐漆-华川涂料 | 郑州律师咨询-郑州律师事务所_河南锦盾律师事务所 | 一体化隔油提升设备-餐饮油水分离器-餐厨垃圾处理设备-隔油池-盐城金球环保产业发展有限公司 | 空气能采暖,热泵烘干机,空气源热水机组|设备|厂家,东莞高温热泵_正旭新能源 | 传递窗_超净|洁净工作台_高效过滤器-传递窗厂家广州梓净公司 | 北京康百特科技有限公司-分子蒸馏-短程分子蒸馏设备-实验室分子蒸馏设备 | 洛阳网站建设_洛阳网站优化_网站建设平台_洛阳香河网络科技有限公司 | 钢绞线万能材料试验机-全自动恒应力两用机-混凝土恒应力压力试验机-北京科达京威科技发展有限公司 | SMN-1/SMN-A ABB抽屉开关柜触头夹紧力检测仪-SMN-B/SMN-C-上海徐吉 | 不锈钢螺丝 - 六角螺丝厂家 - 不锈钢紧固件 - 万千紧固件--紧固件一站式采购 | 网站制作优化_网站SEO推广解决方案-无锡首宸信息科技公司 | 无锡网站建设-做网站-建网站-网页设计制作-阿凡达建站公司 | 噪声治理公司-噪音治理专业隔音降噪公司| RO反渗透设备_厂家_价格_河南郑州江宇环保科技有限公司 | 定硫仪,量热仪,工业分析仪,马弗炉,煤炭化验设备厂家,煤质化验仪器,焦炭化验设备鹤壁大德煤质工业分析仪,氟氯测定仪 | 西安微信朋友圈广告投放_微信朋友圈推广_西安度娘网络科技有限公司 | 数显恒温培养摇床-卧式/台式恒温培养摇床|朗越仪器 | 商用绞肉机-熟肉切片机-冻肉切丁机-猪肉开条机 - 广州市正盈机械设备有限公司 | 上海盐水喷雾试验机_两厢式冷热冲击试验箱-巨怡环试 | 凝胶成像仪,化学发光凝胶成像系统,凝胶成像分析系统-上海培清科技有限公司 | 安徽控制器-合肥船用空调控制器-合肥家电控制器-合肥迅驰电子厂 安徽净化板_合肥岩棉板厂家_玻镁板厂家_安徽科艺美洁净科技有限公司 | 非标压力容器_碳钢储罐_不锈钢_搪玻璃反应釜厂家-山东首丰智能环保装备有限公司 | 上海橡胶接头_弹簧减震器_金属软接头厂家-上海淞江集团 | 纯水电导率测定仪-万用气体检测仪-低钠测定仪-米沃奇科技(北京)有限公司www.milwaukeeinst.cn 锂辉石检测仪器,水泥成分快速分析仪-湘潭宇科分析仪器有限公司 手术室净化装修-手术室净化工程公司-华锐手术室净化厂家 | 钛合金标准件-钛合金螺丝-钛管件-钛合金棒-钛合金板-钛合金锻件-宝鸡远航钛业有限公司 | 深圳彩钢板_彩钢瓦_岩棉板_夹芯板_防火复合彩钢板_长鑫 | 工业rfid读写器_RFID工业读写器_工业rfid设备厂商-ANDEAWELL | 华中线缆有限公司-电缆厂|电缆厂家|电线电缆厂家 | 深圳公司注册-工商注册公司-千百顺代理记账公司 | 耐磨陶瓷,耐磨陶瓷管道_厂家-淄博拓创陶瓷科技 | 海鲜池-专注海鲜鱼缸、移动海鲜缸、饭店鱼缸设计定做-日晟水族厂家 | ORP控制器_ORP电极价格-上优泰百科| 手板_手板模型制作_cnc手板加工厂-东莞天泓 | 消泡剂-水处理消泡剂-涂料消泡剂-切削液消泡剂价格-东莞德丰消泡剂厂家 | 重庆波纹管|重庆钢带管|重庆塑钢管|重庆联进管道有限公司 | 济南货架定做_仓储货架生产厂_重型货架厂_仓库货架批发_济南启力仓储设备有限公司 | 接地电阻测试仪[厂家直销]_电缆故障测试仪[精准定位]_耐压测试仪-武汉南电至诚电力设备 | 建大仁科-温湿度变送器|温湿度传感器|温湿度记录仪_厂家_价格-山东仁科 | 大型低温冷却液循环泵-低温水槽冷阱「厂家品牌」京华仪器_京华仪器 |