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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

带你图文解析ArrayList源码

發布時間:2024/1/18 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 带你图文解析ArrayList源码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

/? ?今日科技快訊? ?/

近日,在納斯達克交易中,蘋果股價下跌了近2%,此前該公司表示,新冠肺炎疫情的影響將使其無法實現本季度的銷售目標。這一消息并非完全出乎意料,因為蘋果受新冠肺炎疫情的影響已經有幾周時間,其后果是導致了一些生產延遲和中國大陸零售店關閉。

/? ?作者簡介? ?/

本篇文章轉載自李詩雨的博客,對ArrayList的源碼進行了圖文并茂的解析,希望對大家有所幫助!

作者的博客地址:

https://juejin.im/user/582c7f462e958a0069b6f068

/? ?前言? ?/

昨天玩了很久的arraycopy,今天讓我們來看看ArrayList的源碼吧。是的,又到了發揮我拙劣的畫技的時候了~

先預覽一下本篇文章的大綱:

下面我們就開始ArrayList的源碼圖解之旅吧,先從增刪改查講起,然后再講ArrayListIterator的源碼,當然這之間我們還會穿插講一下幾個大坑及注意事項~

/? ?增? ?/

ArrayList添加元素的操作,涉及到2個方法 add(E object)?和 add(int index, E object)?。

add(E object) 直接在尾部添加一個元素

add(E object)這個方法,是直接添加一個元素,是在尾部進行插入。

如果原數組的大小不夠會先進行擴容。最終將數據添加在尾部,同時大小加1。源碼如下:

add(int index, E object) 在指定位置添加一個元素

add(int index, E object)這個函數是指在 index的位置 ,插入一個新元素。

我們先看插入操作的核心步驟:

從要插入的位置開始的所有數據,都要往先后挪一位;然后再把要插入的數據放進去。畫了個圖,方便大家理解:

了解了插入的核心步驟之后,我們就來看看 add(int index, E object)的源碼中,插入一個元素到指定位置具體是怎么實現的吧。

一、要先檢測要插入的位置index是否合法。

二、判斷數組是否夠用:

如果數組夠用,即 s<a.length時,直接調用arraycopy(),將從index開始的所有數據都往后挪一位。(關于arraycopy的使用我上一篇已經詳細講過了,所以這里我們應該知道他是從后往前依次往后挪一位過去的)。

如果數組滿了,即s>=a.length時,這里就要:

  • 先將原數組進行擴容,生成新的數組;

  • 將原數組中index之前的數據復制到新數組對應的位置中去。

  • 將原數組中index之后的數據往后挪一位的移動到新的數組中去。

  • 將新數組賦給array。

  • 三、將要添加的元素放在index的位置,同時有效數據個數size+1。

    源代碼見下圖:

    補充:擴容規則

    我們都知道,數組的大小是不可變的,而ArrayList的大小是可變的。而ArrayList底層也是用到了數組,那ArrayList是如何做到大小可以動態變化的呢?

    答案就是通過擴容的方式。

    也就是Object[] newArray = new Object[newCapacity(s)];這句代碼。

    現在我們來具體看看newCapacity(s)的實現:

    這里我們做一下解釋:

    當目前的容量currentCapacity<6 時,increment=12;否則的話 increment等于currentCapacity的2倍。最終返回的大小是currentCapacity+increment。

    也就是說,擴容之后要么是在原有的基礎上 +12,要么就是擴大為原來的三倍。

    /? ?刪? ?/

    ArrayList的刪除操作,我們也來看兩個remove(Object object)和remove(int index)。

    remove(int index) 刪除指定位置的元素


    同樣,我們先來看看刪除的核心操作:


    對應的代碼就是System.arraycopy(a, index + 1, a, index, --s - index);這句代碼,


    即 :如果要刪除【index】位置的元素,那就要把【index】之后的所有元素都往前挪一位,覆蓋掉index原本的位置。


    同樣來畫個圖來幫助大家理解:

    了解了核心的操作之后,我們就來看看 remove(int index)?的源代碼吧:

    remove(Object object) 刪除某個已知元素

    上面我們已經知道了刪除指定位置元素的操作,那如果要刪除某個已知元素的話,我們是不是也應該先找到它對應的位置,然后再進行刪除呢。

    那問題來了,怎么找到元素對應的位置呢?

    對,通過循環遍歷,并進行比較 找到對應的index。

    ▲有個坑!

    ▲【注意】:

    調用remove方法, 會, 且只會 刪除第一個與傳入對象通過equals方法判斷相等的元素。

    如果傳入null,則刪除掉第一個null元素。所以,如果自定義類想要使用remove方法從列表刪除某個指定值對象,還需要實現該類型自己的equals方法才行!

    ▲還有個坑!

    ArrayList是可以順序刪除節點的,但是!如果使用普通for循環,必須是從后往前刪。不能從前往后刪。

    我們先來看一下【錯誤示范】:

    ArrayList?list=new?ArrayList(); list.add("a"); list.add("b"); list.add("c");System.out.println("刪除前:"+list.toString());//順序刪除節點錯誤示范:從前往后刪----會刪不干凈 for?(int?i=0;i<list.size();i++){list.remove(i); } System.out.println("刪除后:"+list.toString());

    【錯誤結果展示】:

    【出錯原因分析】:

    要順序刪除ArrayList的全部節點,如果我們從前往后的順序刪除,先刪除【0】位置的數據,但是由于刪除的時候是從后往前挪一位進行刪除的,所以【0】的位置又會被下一個位置的數據覆蓋上,實際上【0】還是有數據的。再畫一張圖來方便大家理解:

    【正確的做法】:

    要想順序刪除ArrayList的所有節點,且采用普通的for循環,那只能從后往前刪,這樣就不會出問題。

    /? ?改、查? ?/

    ArrayList修改數據很簡單,調用的是set(int index, E object)方法,直接修改對應位置的數據即可。

    ArrayList獲取數據就更簡單了,由于是順序表有下標,直接取出對應下標數據就可以了。

    /? ?ArrayList的三種遍歷方式? ?/

    ArrayList的遍歷我們有三種方式:for循環,增強for循環?和?迭代器三種方式。

    (當然,增強for循環其實還是用迭代器實現的,這一點我們可以通過反編譯來進行驗證。)

    ArrayList?arrayList?=?new?ArrayList(); arrayList.add("情人節"); arrayList.add("快樂"); arrayList.add("我"); arrayList.add("對"); arrayList.add("自己說~");System.out.println("for循環的方式遍歷:"); for?(int?i?=?0;?i?<?arrayList.size();?i++)?{System.out.print(arrayList.get(i)); }System.out.println(); System.out.println("---------------------------------"); System.out.println("增強for循環的方式遍歷:"); for?(Object?s?:?arrayList)?{System.out.print((String)?s); }System.out.println(); System.out.println("---------------------------------"); System.out.println("迭代器的方式遍歷:"); Iterator<String>?iterator?=?arrayList.iterator(); while?(iterator.hasNext())?{System.out.print(iterator.next()); }

    我們來看一下打印結果:

    /? ?迭代器ArrayListIterator源碼解讀? ?/

    好的,上面我們知道了ArrayList可以使用迭代器進行遍歷。

    那為什么它可以使用迭代器這種方式呢?

    這個我們又要去看源碼了,跟進arrayList.iterator()的 iterator()方法,我們會發現ArrayList有一個內部類ArrayListIterator。而 ArrayListIterator 實現了 Iterator 接口,所以才可以使用迭代器這種方式進行迭代。

    現在我們就來具體看看ArrayListIterator的相關源代碼和注意事項吧。

    首先,我們可以看到ArrayListIterator實現了Iterator這個接口

    提一下什么是 Iterator (迭代器)?

    我們都知道在Java中,有很多的數據容器,這些的操作又有很多的共性。而迭代器就是給各種容器提供了公共的操作接口。這樣就使得對容器的操作有了規范性。

    在Iterator接口中定義了三個方法:

    • hasNext(): 如果仍有元素可以迭代,就返回true.

    • next(): 返回迭代的下一個元素。

    • remove(): 從集合中移除返回的最后一個對象。(可選操作)

    源碼如下:

    ArrayListIterator中的坑

    ArrayListIterator 的源碼其實并不難理解,就是實現了 Iterator 中的三個方法。但是這里有一個▲坑▲大家需要注意,那就是:

    每當我們使用迭代器遍歷元素時,如果使用迭代器以外的方法修改了元素內容(如刪除元素),那就會拋出ConcurrentModificationException的異常。

    讓我先看一下現象,然后再從源碼角度找原因。

    錯誤代碼示例:

    ????????ArrayList?arrayList?=?new?ArrayList();arrayList.add("a");arrayList.add("b");arrayList.add("c");System.out.println("移除前:"?+?arrayList);Iterator<String>?iterator?=?arrayList.iterator();while?(iterator.hasNext())?{if?("c".equals(iterator.next()))?{arrayList.remove("c");}}System.out.println("移除后:"?+?arrayList);//注意增強for使用的也是迭代器//所以以下這種操作也會報ConcurrentModificationException//for?(Object?o?:?arrayList)?{//????arrayList.remove(o);//}//System.out.println("移除后2:"?+ arrayList);

    報錯顯示:

    好的,現象我們已經看到了,那現在我們就來看看錯誤的原因吧。

    我們要先來了解一下這幾個變量的含義:

    然后我們來看一下何種情況下會報錯:

    先分析一下報錯原因:

    在我們使用 ArrayLis 的 iterator() 方法獲取到迭代器進行遍歷時,會把 ArrayList 當前狀態下的 modCount 賦值給 ArrayListIterator類的 expectedModCount 屬性。

    如果我們在迭代過程中,使用了 ArrayList 的 remove()方法,這時 modCount 就會加 1 ,但是迭代器中的expectedModCount 并沒有變化,當我們再使用迭代器的next()方法時,它就會報ConcurrentModificationException的錯。

    最后我們再來比較一下 ArrayListIterator中的 remove()方法和ArrayList自己的remove()方法的不同之處,驗證一下錯誤發生的原因:

    所以我們得到的啟示是:

    每當我們使用迭代器遍歷元素時,要使用迭代器自己的刪除方法,而不能使用迭代器以外的方法修改了元素內容,否則會造成expectedModCount和modCount的值不一致,從而拋出ConcurrentModificationException的異常。

    此外,我們還要注意一下,增強for循環其實也是使用的迭代器,所以也要注意同樣的問題。

    推薦閱讀:

    Kotlin中關于泛型型變的那些事

    Dart語言快速入門

    這份AS快捷鍵大全,讓你的開發效率快10倍

    歡迎關注我的公眾號

    學習技術或投稿

    長按上圖,識別圖中二維碼即可關注

    總結

    以上是生活随笔為你收集整理的带你图文解析ArrayList源码的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。