java 集合modcount_源碼|jdk源碼之LinkedList與modCount字段

鏈表是對上一篇博文所說的順序表的一種實現。

與ArrayList思路截然不同,鏈表的實現思路是:

不同元素實際上是存儲在離散的內存空間中的。

每一個元素都有一個指針指向下一個元素,這樣整個離散的空間就被“串”成了一個有順序的表。

從鏈表的概念來講,它可以算是一種遞歸的數據結構,因為鏈表拿掉第一個元素剩下的部分,依然構成一個鏈表。

時間空間復雜度

通過索引定位其中的一個元素。由于不能像ArrayList那樣直接通過計算內存地址偏移量來定位元素,只能從第一個元素開始順藤摸瓜來數,因此為O(n)。

插入元素。實際上插入元素需要看情況:

如果指定鏈表中某個元素將其插之其后,那么首先得找出該元素對應的節點,還是O(n)。

如果能夠直接指定節點往其后插入(如通過迭代器),那么僅僅需要移動指針即可完成,O(1)。

移除元素。移除和插入類似,得看提供的參數是什么。如果提供的是元素所在的節點,那么也只需要O(1)。

LinkedList的繼承結構

f18af3fdc223a6e1381716ac87470af6.png

首先繼承結構和ArrayList類似,實現了List接口。

但是,它繼承的是繼承了AbstractList類的AbstractSequentialList類,

這個類的作用也是給List中的部分函數提供默認實現,只是這個類對鏈表這種List的實現提供了更多貼合的默認函數實現。

還有可以注意到,LinkedList實現了Deque接口,這也很顯然,鏈表這種結構天然就適合當做雙端隊列使用。

LinkedList源碼分析

節點定義

先來看鏈表的節點定義:

private static class Node {

E item;

Node next;

Node prev;

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

可以看到,鏈表節點除了保存數據外,還需要保存指向前后節點的指針。

這里,鏈表即有后繼指針也有前驅指針,因此這是一個雙向鏈表。

一組節點之間按順序用指針指起來,就形成了鏈表的鏈狀結構。

屬性和構造函數

transient int size = 0;

transient Node first;

transient Node last;

三個屬性,first和last分別指向鏈條的首節點和尾節點。

這樣有個好處,就是鏈表即可以使用頭插法也可以采用尾插法。

size屬性跟蹤了鏈表的元素個數。雖然說遍歷一遍鏈表也能統計到元素個數,

但是那是O(n)的費時操作。

因此,我們可以發現鏈表的size方法是O(1)的時間復雜度。

public LinkedList() {

}

LinkedList的代碼很簡單,構造函數空空如也。

空表中,first和last字段都為null。

get和set方法

public E get(int index) {

checkElementIndex(index);

return node(index).item;

}

public E set(int index, E element) {

checkElementIndex(index);

Node x = node(index);

E oldVal = x.item;

x.item = element;

return oldVal;

}

Node node(int index) {

// assert isElementIndex(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;

}

}

get和set的思路都是先根據索引定位到鏈表節點,然后獲得或設置節點中的數據,這抽象出了node函數,根據索引找到鏈表節點。

node的思路也很顯然,遍歷一遍即可得到。

這里做了一點優化,我們可以發現LinkedList的實現是一個雙向鏈表,并且LinkedList持有了頭尾指針。

那么,根據索引和size就可以知道該節點是在鏈表的前半部分還是后半部分,

從而決定從頭節點開始遍歷還是從尾節點開始遍歷,這樣最多遍歷 N / 2次即可找到。

添加/刪除

public boolean add(E e) {

linkLast(e);

return true;

}

public void add(int index, E element) {

checkPositionIndex(index);

if (index == size)

linkLast(element);

else

linkBefore(element, node(index));

}

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

}

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

}

添加/刪除的思路都類似,刪除的代碼就不貼了。如果能夠提供需要被操作的節點,就能直接移動下指針,O(1)完成。否則就需要遍歷找到這個節點再操作。

需要關注兩點:

有的操作是操作頭指針,有的操作是操作尾指針。但是不管操作哪一個,都需要維護另外一個指針及size的值。

如果是刪除,刪除后及時把相關節點的item字段置為null,以幫助gc能更快的釋放內存。

modCount字段分析

之前閱讀ArrayList的代碼時發現了modCount這一字段,它是定義在AbstractList類中的。之前不知道它起到什么作用,這次給弄明白了。

迭代器

迭代器迭代中表被修改

考慮以下這段代碼:

List list = new LinkedList<>();

Iterator it = list.listIterator();

list.add(1);

it.next();

在迭代器創建之后,對表進行了修改。這時候如果操作迭代器,則會得到異常java.util.ConcurrentModificationException。

這樣設計是因為,迭代器代表表中某個元素的位置,內部會存儲某些能夠代表該位置的信息。當表發生改變時,該信息的含義可能會發生變化,這時操作迭代器就可能會造成不可預料的事情。

因此,果斷拋異常阻止,是最好的方法。

實際上,這種迭代器迭代過程中表結構發生改變的情況,更經常發生在多線程的環境中。

記錄表被修改的標記

這種機制的實現就需要記錄表被修改,那么思路是使用狀態字段modCount。

每當會修改表的操作執行時,都將此字段加1。使用者只需要前后對比該字段就知道中間這段時間表是否被修改。

如linkedList中的頭插和尾插函數,就將modCount字段自增:

private void linkFirst(E e) {

final Node f = first;

final Node newNode = new Node<>(null, e, f);

first = newNode;

if (f == null)

last = newNode;

else

f.prev = newNode;

size++;

modCount++;

}

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

}

迭代器

迭代器使用該字段來判斷,

private class ListItr implements ListIterator {

private Node lastReturned;

private Node next;

private int nextIndex;

private int expectedModCount = modCount;

ListItr(int index) {

// assert isPositionIndex(index);

next = (index == size) ? null : node(index);

nextIndex = index;

}

public boolean hasNext() {

return nextIndex < size;

}

public E next() {

checkForComodification();

if (!hasNext())

throw new NoSuchElementException();

lastReturned = next;

next = next.next;

nextIndex++;

return lastReturned.item;

}

/* ... */

public void set(E e) {

if (lastReturned == null)

throw new IllegalStateException();

checkForComodification();

lastReturned.item = e;

}

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

}

迭代器開始時記錄下初始的值:

private int expectedModCount = modCount;

然后與現在的值對比判斷是否被修改:

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

這是一個內部類,隱式持有LinkedList的引用,能夠直接訪問到LinkedList中的modCount字段。

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

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

相關文章

idea 新建ssm java ee_IDEA搭建SSM項目實現增刪改查

首先打開IDEA&#xff0c;File—>New—>Project創建項目選擇左側導航欄里的Maven&#xff0c;勾上勾&#xff0c;選擇webapp按如下圖進行填寫創建完成后進入項目&#xff0c;右下角彈出的提示點擊右邊的Enable Auto-Import&#xff0c;自動配置連接數據庫&#xff0c;我用…

php mail centos_centos怎么發送郵件

一、安裝sendmail與mail1、安裝sendmail&#xff1a;1) centos下可以安裝命令&#xff1a;yum -y install sendmail2) 安裝完后啟動sendmail命令&#xff1a;service sendmail start2、安裝mail安裝命令&#xff1a;yum install -y mailx二、發送郵件1、通過文件內容發送發送命…

php文件的作用,php入口文件的作用-PHP問題

php入口文件的作用php入口文件能夠完成主動加載性能。解析PHP入口文件的主動加載性能php的主動加載&#xff1a;正在php5之前&#xff0c;咱們要用某個類或類的辦法&#xff0c;那必需include或許require&#xff0c;之后能力應用&#xff0c;每一次用一個類&#xff0c;都需求…

emacs php 配置文件,如何配置emacs進行正確的PHP開發?

我使用web模式(http://web-mode.org/)混合HTML / PHP文件和php模式為純PHP文件.最新版本的php-mode還推薦使用混合HTML / PHP文件的Web模式&#xff1a;https://github.com/ejmr/php-mode#avoid-html-template-compatibility.不同于其他模式,如mmm模式,mumamo或多網絡模式,嘗試…

php 5.3.9 漏洞,PHP-5.3.9遠程執行任意代碼漏洞(CVE-2012-0830) 詳解

這個新的修復方法初衷是好的, 但是卻帶來一個嚴重的問題(5.3.10中已經修復), 這個問題最初是由Stefan Esser發現的. 請看之前(5.3.9)最終的修復方案(php_register_variable_ex):代碼如下while (1) {if (zend_symtable_find(symtable1, escaped_index, index_len 1, (void **) …

java中隨機數邊界問題,java 簡單Dice問題(隨機數的運用)

[java]代碼庫/*** Dice Write a program that simulates rolling two dice using the following* steps: 1. Prompt the user for the number of sides for two dice. 2. “Roll” the* dice three times by generating a random number between 1 (inclusive) and the* number…

php 正則替換 ubb,php實現過濾UBB代碼的類

本文實例講述了php實現過濾UBB代碼的類。分享給大家供大家參考。具體如下&#xff1a;PHP代碼如下&#xff1a;class Day{function ubb($Text) { /// UBB代碼轉換//$Texthtmlspecialchars($Text);//$Textereg_replace("\r\n","",$Text);$Textereg_rep…

java單詞測試,java單詞 - 在線打字測試(dazi.kukuw.com)

java單詞貢獻者&#xff1a;15533470608類別&#xff1a;英文 時間&#xff1a;2018-08-04 22:32:16 收藏數&#xff1a;20 評分&#xff1a;0返回上頁舉報此文章請選擇舉報理由&#xff1a;廣告/謠言/欺詐政治敏感色情/違法信息垃圾文章其他收藏到我的文章改錯字public static…

java vector list,Java基礎之:List——ArrayList Vector

Java基礎之&#xff1a;List——ArrayList & VectorArrayList簡單介紹ArrayList實現了List接口&#xff0c;底層是一個數組&#xff0c;并實現了可變的功能。底層屬性(transient Object[] elementData;)在序列化時&#xff0c;忽略該屬性。ArrayList實現了List接口&#xf…

java建立線性表的鏈式結構,數據結構學習----線性表的鏈式表示(Java實現)

線性表接口LList&#xff1a;package com.clarck.datastructure.linked;/*** 線性表接口LList&#xff0c;描述線性表抽象數據類型&#xff0c;泛型參數T表示數據元素的數據類型** author clarck**/public interface LList {/*** 判斷線性表是否空* return*/boolean isEmpty();…

php prepare 批量,PreparedStatement批處理

PreparedStatement批量更新關鍵代碼 無 import java.sql.Connection;import java.sql.PreparedStatement; //...String sql "insert into employee (name, city, phone) values (?, ?, ?)";Connection connection new getConnection();PreparedStatement pPrepa…

釘釘 php 推送,微信模板推送,釘釘信息推送

上午的時候看到有朋友需要微信推送&#xff0c;正好我也需要&#xff0c;之前一直用 Server 醬的&#xff0c;但是最近用不了&#xff0c;想找一個替代品&#xff0c;一開始準備選擇釘釘&#xff0c;除了打卡&#xff0c;我很少使用釘釘&#xff0c;郵件提醒是備用方案&#xf…

java repaint 重畫圖形,學習筆記:WINDOWS的圖形重繪基礎

OnPaint()與OnDraw()的區別&#xff1a;OnPaint是WM_PAINT的消息響應函數&#xff0c;在MFC的基類里OnPaint函數調用了OnDraw()函數。OnPaint函數另外還調用了OnPrepareDC()函數。如果在窗口子類覆蓋了OnPaint函數&#xff0c;當MFC調用我們重寫的OnPaint函數時&#xff0c;就調…

php定義數據表類,phpwind中的數據庫操作類

phpwind中的數據庫操作類2021-01-22 20:12:15141/*來源&#xff1a;phpwind.net*/ClassDB{var$query_num0;functionDB($dbhost,$dbuser,$dbpw,$dbname,$pconnect0){$this->connect($dbhost,$dbuser,$dbpw,$dbname,$pconnect);}functionconnect($dbhost,$dbuser,$dbpw,$dbnam…

渦輪機葉片matlab強度分析論文,一種基于MATLAB及Pro_E的渦輪建模方法

自動化與控制與二一種基于&#xff2d;&#xff21;&#xff34;&#xff2c;&#xff21;&#xff22;及&#xff30;&#xff52;&#xff4f;&#xff0f;&#xff25;的渦輪建模方法王智明(中海油服油田技術事業部北京&#xff11;&#xff10;&#xff11;&#xff11;&am…

基于matlab的傳熱學虛擬實驗開發,基于MATLAB的傳熱學課程虛擬實驗軟件的開發

215教育現代化2018 年 12 月第 49 期 教育信息技術 基于 MATLAB 的傳熱學課程虛擬實驗軟件的開發 周永利&#xff0c;李友榮&#xff0c;石萬元&#xff0c;張力元&#xff0c;楊晨&#xff0c;卞煜&#xff0c;王國強&#xff0c;李俊&#xff0c;包鍵 ( 重慶大學 低品位能源利…

java做 binggo,Linux啟動與停止spring boot工程的腳本示例

在springboot項目啟動有三種方式&#xff1a;1、運行主方法程序2、使用命令mvn spring-boot:run 在命令行運行3、使用 mvn packpage打包位jar文件以后&#xff0c;使用java -jar yourapp.jar命令行運行一般我們在開發的時候經常使用的是前面兩種運行方式&#xff0c;在部署實施…

php計劃任務 框架,計劃任務的使用 ThinkCMF內容管理框架,做最簡約的ThinkPHP開源軟件...

1、先不管是是否是獨立分組&#xff0c;必須在Application\common\項目名下的Conf文件夾內創建2個文件一個是tags.php(項目默認有&#xff0c;直接加入需要執行的代碼即可) 一個是 crons.php。注意這兩個文件名為thinkphp標準文件名&#xff0c;不可以改變tages.php內容是&…

php按文章評論數排序,zblog獲取分類文章排序按指定的時間排序、評論數量排序、瀏覽數量排序...

Zblog PHP在1.8版本的時候想要調用多個分類的文章&#xff0c;并且按照自己的需求去排序是很簡單的事情&#xff0c;很多博友也利用這個方法進行最新文章排行、熱門評論文章排行等等操作&#xff0c;現在隨著ZblogPHP版本的升級&#xff0c;已經封裝了數據庫語句&#xff0c;導…

蟻群算法matlab vrp問題車輛限重,蟻群算法MATLAB解VRP問題

Excel exp12_3_2.xls內容&#xff1a;ANT_VRP函數&#xff1a;function [R_best,L_best,L_ave,Shortest_Route,Shortest_Length]ANT_VRP(D,Demand,Cap,iter_max,m,Alpha,Beta,Rho,Q)%% R_best 各代最佳路線%% L_best 各代最佳路線的長度%% L_ave 各代平均距離%% Shortest_Rout…