JDK源碼學習之Arraylist與LinkedList

ArrayList和LinkedList是我們在開發過程中常用的兩種集合類,本文將從底層源碼實現對其進行簡單介紹。

下圖是Java集合類所涉及的類圖。

JDK源碼學習之Arraylist與LinkedList

一.ArrayList

從上面的集合類圖可以看出,ArrayList實現了List接口。ArrayList是順序的集合容器,容器中可以存放null元素,而底層則是通過一個可自動增長大小的動態數組實現的。ArrayList不是線程安全的,也沒有實現同步。

1.1 ArrayList操作性能

訪問數組中第 n 個數據的時間花費是?O(1),但是要在數組中查找一個指定的數據則是 O(N)。當向數組中插入或者刪除數據的時候,最好的情況是在數組的末尾進行操作,時間復雜度是?O(1),但是最壞情況是插入或者刪除第一個數據,時間復雜度是O(N)。在數組的任意位置插入或者刪除數據的時候,后面的數據全部需要移動,移動的數據還是和數據個數有關所以總體的時間復雜度仍然是O(N)。

JDK源碼學習之Arraylist與LinkedList

1.2 私有屬性

ArrayList有兩個私有屬性,一個是實現數據存儲的數組,一個是表示數組中元素的個數。值得注意的是關鍵字transient。

JDK源碼學習之Arraylist與LinkedList

ArrayList這個類實現了Serializable接口。Java的Serializable提供了一種持久化對象實例的機制,當持久化對象時,可能某個特殊的對象數據成員我們不想讓其用Serializable機制保存它,可以使用關鍵字transient來進行屏蔽。此外還有個保護的屬性:modCount,含義為已從結構上修改此列表的次數。從結構上修改是指更改列表的大小,或者打亂列表,從而使正在進行的迭代產生錯誤的結果。

1.3 構造方法

ArrayList中有三個構造函數,一個是默認構造一個容量為10的數組,一個指定容量的空列表和一個Collection類型的空列表。我們可以在使用時通過指定我們預估到的數組容量,來減少擴容次數

1.4 數組擴容

每一個ArrayList的實例都有一個容量,用來表示存儲數據的數組大小。容器內的元素不能大于當前當前的容器大小。當向容器中添加數據時,若容器的容量不足,容器會自動擴容。通過對比jdk1.7的ArrayList源碼發現,擴容兩個方法是不一樣的,jdk1.6中使用的是除法對其容量進行計算(加0.5倍),而jdk1.7中則使用的是移運算。

JDK源碼學習之Arraylist與LinkedList

位運算是CPU直接操作的,除法等四則運算都是基于移位運算的,所以當有大量計算的時候,移位運算可以大大節約CPU的時間。通過1千萬次位運算和四則運算做對比數據結果如下:

JDK源碼學習之Arraylist與LinkedList

可以看出,在計算大量數據的情況下,移位運算的花費時間比乘除法快很多。

1.5 數組復制方法

JDK源碼學習之Arraylist與LinkedList

可以看到在ensureCapacity(intminCapacity)中調用的是Arrays的copyOf()方法,而在add方法中,調用的數組復制方法為:

native void arraycopy();Arrays的copyOf方法的實現:

JDK源碼學習之Arraylist與LinkedList

實現copyOf的時候會新創建一個大小為newCapcity的數組,然后將舊的elementData放入其中。

1.6 小節

1、通過查看ArrayList的源碼,注意到有三個不同的構造方法,合理使用構造方法能減少數組擴容拷貝造成的額外開銷。

2、ArrayList大量調用了Arrays.copyOf和System.arrayCopy的方法,注意這兩個方法的區別。

3、jdk1.6和1.7中數組擴容的方法不一致,注意比較有差異的地方。

二.LinkedList

LinkedList與ArrayList一樣實現List接口,只是ArrayList是List接口的大小可變數組的實現,LinkedList是List接口鏈表的實現。基于鏈表實現的方式使得LinkedList在插入和刪除時更優于ArrayList,而隨機訪問則比ArrayList遜色些。

除了實現 List 接口外,LinkedList類還實現?Deque接口,為 add、poll 提供先進先出隊列操作,以及其他堆棧和雙端隊列操作。

LinkedList定義:

public class LinkedList<E > extends AbstractSequentialList<E>

implements List<E>,Deque<E>, Cloneable, java.io.Serializable

2.1 底層數據結構

與ArrayList的區別在于,LinkedList底層是基于雙向鏈表實現的:

以上的數據結構可以從LinkedList的私有屬性看出:

private transient Entry<E> header = newEntry<E>(null, null, null);//頭結點是不存放元素的

private transient int size = 0;//雙向循環鏈表的大小

2.2 雙向循環列表的操作性能

對于雙向循環鏈表的插入和刪除操作只是多移動幾個指針。

備注:這里只是單純的描述雙向鏈表這種數據結構的插入和刪除性能,下文將對比ArrayList與LinkedList的性能。

2.3 構造函數

LinkedList類有兩個構造函數,一個是無參數的,一個是構造任意類型的集合類的列表

該構造函數構造一個空的列表,header頭結點表示如下, 形成一個閉環。

有參構造方法,參數為collection的c,this()調用默認的無參構造函數,然后再調用addAll()方法,將c中的元素添加加入列表。

三.LinkedList與ArrayList比較

1.ArrayList是基于數組的數據結構,LinkedList是基于鏈表的數據結構。

2.ArrayList內部的元素可以直接通過get與set方法進行訪問,因為ArrayList本質上就是一個數組.但LinkedList在get與set方面弱于ArrayList.

當然,這些對比都是指數據量很大或者操作很頻繁的情況下的對比,如果數據和運算量很小,那么對比將失去意義.

3.此外 LinkedList 還實現了?Queue?接口,該接口比List提供了更多的方法,包括?offer(),peek(),poll()等.

注意: 默認情況下ArrayList的初始容量非常小,所以如果可以預估數據量的話,分配一個較大的初始值屬于最佳實踐,這樣可以減少調整大小的開銷。

4.對于ArrayList與LinkedList性能下圖做個簡單比較

* 表中的 add() 代表 add(E e),而 remove()代表 remove(int index)

ArrayList 對于隨機位置的add/remove,時間復雜度為 O(n),但是對于列表末尾的添加/刪除操作,時間復雜度是 O(1). 而LinkedList對于隨機位置的add/remove,時間復雜度為 O(n),但是對于列表 末尾/開頭 的添加/刪除操作,時間復雜度是 O(1).

下面是測試代碼:

JDK源碼學習之Arraylist與LinkedList

輸出結果截圖如下:

JDK源碼學習之Arraylist與LinkedList

上述測試可以看出LinkedList在 進行add和remove操作時更快,而在進行get操作時較慢.

通過本次源碼學習之旅,我從LinkedList、ArrayList的底層結構出發,更深刻地了解了這兩個類的一些基本操作方法,文章中有不周全的地方歡迎指正,共同學習。

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

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

相關文章

學習記錄4

學習了python基本數據類型&#xff0c;附學習筆記圖及操作圖 轉載于:https://www.cnblogs.com/bgd140206127/p/6549229.html

self 實例對象-代碼詳細解釋

self代表類的實例&#xff0c;而非類哪個對象調用方法&#xff0c;那么該方法中的self就代表那個對象self.__calss__ 代表類名class Person(object):def run(self):print("run")print(self.__class__)p self.__class__("tt",30,10,20)print(p)def eat(sel…

CString之GetBuffer與ReleaseBuffer

我們知道&#xff0c;CString是MFC中提供的方便字符串操作的一個類&#xff0c;非常好使&#xff0c;具有自動動態內存管理功能。 GetBuffer()主要作用是將字符串的緩沖區長度鎖定&#xff1b; ReleaseBuffer()則是解除對緩沖區的鎖定&#xff0c;這樣使得CString對象在以后的代…

mac 編譯android系統,mac 編譯 Android 系統雜記

掛載android分區sudo hdiutil attach ~/android_code/android7.dmg.sparseimage -mountpoint /Volumes/android原放入U盤&#xff1a;echo 188jinghao | sudo -S hdiutil attach ~/android7.dmg.sparseimage -mountpoint /Volumes/android放入機械硬盤sudo hdiutil attach /Vol…

Java開發必須熟悉的Linux命令總結

身為一個Java開發人員&#xff0c;這些常用的Linux命令必須掌握。即使平時開發過程中沒有使用Linux&#xff08;Unix&#xff09;或者mac系統&#xff0c;也需要熟練掌握Linux命令。因為很多服務器上都是Linux系統。所以&#xff0c;要和服務器機器交互&#xff0c;就要通過she…

構析函數

析構函數&#xff1a;__del__() 釋放對象時自動調用 class Person(object):def run(self):print("run")def eat(self,food):print("eat"food)def __init__(self,name,age,height,weight):self.name nameself.height heightself.age ageself.weight …

Java 序列化Serializable詳解(附詳細例子)

Java 序列化Serializable詳解&#xff08;附詳細例子&#xff09; 1、什么是序列化和反序列化Serialization&#xff08;序列化&#xff09;是一種將對象以一連串的字節描述的過程&#xff1b;反序列化deserialization是一種將這些字節重建成一個對象的過程。 2、什么情況下需要…

kettle-實現每個分組的前N的數據

2019獨角獸企業重金招聘Python工程師標準>>> 第一步&#xff1a;創建表及數據&#xff1a; create table uid(uid int, --uidcate varchar(20), --類別price double --金額 ) insert into uid values(123,c1,21); insert into uid values(123,c2,23); insert into u…

重寫__repr__與__str__函數

重寫&#xff1a;將函數重新定義寫一遍__str__():再調用print 打印對象時自動調用&#xff0c;是給用戶用的是一個描述對象的方法__repr__():是給機器用的&#xff0c;在python解釋器里面直接敲對象名再回車調用的方法注意&#xff1a;在沒有str時&#xff0c;且有repr,str re…

linux nexus 使用問題

2019獨角獸企業重金招聘Python工程師標準>>> 問題一&#xff0c;啟動提示設置RUN_AS_USERroot 但是&#xff0c;設置export或 /etc/profile未生效。 **************************************** WARNING - NOT RECOMMENDED TO RUN AS ROOT *************************…

項目回顧-PopupWindow

右上菜單&#xff0c;可以通過 重寫 onCreateOptionsMenu指定 menu&#xff0c; 重寫 onOptionsItemSelected 來響應點擊事件 不過 這個菜單在某些手機上彈出的有點卡頓&#xff0c;而且如果不對主題進行設置&#xff0c;會從actionbar 上直接彈出&#xff0c;而不是下面 如果想…

android listpreference 自定義,Android ListPreference的用法一

xmlns:android"http://schemas.android.com/apk/res/android"android:key"screen_list"android:title"標題"android:summary"說明摘要">< ListPreferenceandroid:key"myListPreference"android:title"標題"…

C語言求最大公約數和最小公倍數的幾種算法

求最小公倍數算法&#xff1a; 最小公倍數兩整數的乘積最大公約數 求最大公約數算法&#xff1a; (1)輾轉相除法 有兩整數a和b&#xff1a; ① a%b得余數c ② 若c0&#xff0c;則b即為兩數的最大公約數 ③ 若c≠0&#xff0c;則ab&#xff0c;bc&#xff0c;再回去執行①…

3月15日云棲精選夜讀:雙管齊下,MaxCompute數據上云與生態

雙管齊下&#xff0c;MaxCompute數據上云與生態 作者&#xff1a;場景研讀 Go語言并發機制初探 作者&#xff1a;邴越 趣拍云短視頻SDK全面升級&#xff0c;簡單易用引開發者點贊 作者&#xff1a;sherry是雪梨 發表在&#xff1a;趣拍云團隊 阿里云機器學習平臺編程模型演…

qt android glsl,基于Qt的OpenGL學習(1)—— Hello Triangle

簡介要學習OpenGL的話&#xff0c;強烈安利這個教程JoeyDeVries的learnopengl&#xff0c;這里是中文翻譯好的版本。教程中使用OpenGL是通過GLFW這個庫&#xff0c;而在Qt中對OpenGL封裝得很好&#xff0c;并且和GUI以及IO相關的處理Qt更便捷&#xff0c;學習起來更輕松。這里就…

解決:Not Found: /favicon.ico

直接說解決辦法&#xff1a; &#xff08;1&#xff09;制作一個 favicon.ico圖標放在<head></head>標簽中 <link rel"shortcut icon" href"xxxxxxxxxx.ico" type"image/x-icon" /> <!--制作的圖標&#xff0c;使用hr…

多態方法調用的解析和分派

方法調用并不等同于方法執行&#xff0c;方法調用階段唯一的任務就是確定被調用方法的版本&#xff08;即調用哪一個方法&#xff09;&#xff0c;暫時還不涉及方法內部的具體運行過程。在程序運行時&#xff0c;進行方法調用是最普遍、最頻繁的操作&#xff0c;Class文件的編譯…

ES6:Set和Map

Set Set:類似數組&#xff0c;但是成員的值都是唯一的&#xff0c;沒有重復。Set本身是一個構造函數&#xff0c;用來生成Set數據結構。他包含的方法&#xff1a;add: 添加某個值&#xff0c;返回Set結構本身。delete: 刪除某個值&#xff0c;返回一個布爾值&#xff0c;表示是…

九九乘法表[循環嵌套]

#九九乘法表 # 1*11 # 1*22 2*24 # 1*33 2*36 3*39 # ...#循環嵌套 #行數 i 1 while i < 9:# 打印每行的內容j 1while j < i:print("%d * %d %3d " % (i, j, i * j), end)j 1print() # 換行i 1while嵌套&#xff1a;w 1 while w < 10: #外層循…

關于用VS寫C程序運行時出現燙字以及亂碼的問題的原因

最近在復習C語言寫程序時&#xff0c;突然碰到標題上的這種情況&#xff0c;后來經過上網查找以及逐步調試才發現原來是在打印數組的時候“越界”導致的&#xff0c;因為程序在默認初始化char類型的數組時&#xff0c;初始化的值是“燙”字&#xff0c;一般情況下是字符串未初始…