Java ArrayList源碼分析(有助於理解數據結構)

来源:https://www.cnblogs.com/xiaozhongfeixiang/archive/2019/09/12/11514674.html
-Advertisement-
Play Games

arraylist源碼分析 1.數組介紹 數組是數據結構中很基本的結構,很多編程語言都內置數組,類似於數據結構中的線性表 在java中當創建數組時會在記憶體中劃分出一塊連續的記憶體,然後當有數據進入的時候會將數據按順序的存儲在這塊連續的記憶體中。當需要讀取數組中的數據時,需要提供數組中的索引,然後數組根據 ...


arraylist源碼分析

1.數組介紹

數組是數據結構中很基本的結構,很多編程語言都內置數組,類似於數據結構中的線性表

在java中當創建數組時會在記憶體中劃分出一塊連續的記憶體,然後當有數據進入的時候會將數據按順序的存儲在這塊連續的記憶體中。當需要讀取數組中的數據時,需要提供數組中的索引,然後數組根據索引將內

存中的數據取出來,返回給讀取程式。在Java中並不是所有的數據都能存儲到數組中,只有相同類型的數據才可以一起存儲到數組中。

  

 

 

 因為數組在存儲數據時是按順序存儲的,存儲數據的記憶體也是連續的,所以他的特點就是:定址讀取數據比較容易,插入和刪除比較困難。

 

帶參構造方法

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
       //創建指定長度的數組
this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) {
       //空數組
this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);} //如果既不大於零,也不小於零,就會報錯拋出異常 }

 

空參構造方法

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //給一個成員變數賦值
    }
  private static final Object[] DEFAULTCAPACITY EMPTY ELEMENTDATA={}; //空參構造方法創建的數組就是一個空的

 

下麵這個用的不多,能看懂即可

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray(); //把Collection這個集合也變成了一個數組,傳給了當前數組elementData
     //大於0,就拷貝數組
if ((size = elementData.length) != 0) { //判斷當前elementData數組的長度,如果不等於0 if (elementData.getClass() != Object[].class) //判斷類型是不是Object數組類型,如果不是這個類型 elementData = Arrays.copyOf(elementData, size, Object[].class); //拷貝一個數組,然後長度,然後把Object[].class數組傳進去,然後生成一個elementData數組
     //不是大於0,就拷貝一個空的 }
else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; //設置為空數組 } }

 

 

 添加元素

public boolean add(E e) {
    //檢測是否需要擴容 下¹ensureCapacityInternal(minCapacity:size
+ 1); // size是當前存的數據個數
    //數組賦值
elementData[size++] = e; return true; }

 

 擴容的入口方法,計算容量

private void 上¹ensureCapacityInternal(int minCapacity){    //minCapacity最小值,傳的時候就是+1後的
下下³ensureExplicitCapacity(下²calculateCapacity(elementData,minCapacity)); //計算容量,傳入一個數組elementData,再傳一個最小容量minCapacity

 

計算最小容量

private static int 上²calculateCapacity(Object[] elementData, int minCapacity){
//如果elementData是空的
if(elementData==DEFAUL TCAPACITY EMPTY EL EMENTDAT)|
//返回一個預設或者minCapacity,最後是返回其中最大的
return Math. max(DEFAULT_CAPACIT minCapacity); 
//不是空,就返回size+1的值 
return minCapacity;
}

 

 

private void 上上³ensureExplicitCapacity(int minCapacityp{
//只要發生修改就+1 modCount
++;
//是否滿足擴容的條件 最小容量(當前數組存值得容量?) - object數組的長度
if(minCapacity-elementData. length〉0)//elementData.lenth=10 (預設容量為10)
下¹grow(minCapacity);

 

 數組擴容方法

private void 上¹grow(int minCapacity){
   //計算當前數組容量,也就是老的容量
   int oldCapacity=elementData. length;
   //新的數組容量 = 老容量 + 老容量/2 (老容量的1.5倍)

  int newCapacity=oldCapacity+(oldCapacity >>1); >>1 除二

     // oldcapacity=10
    // oldcapacity >>1
    // 0000 1010 >> 1
    // 0000 0101 = 5

    //判斷新的數組容量-之前最小容量

   if(newCapacity-minCapacity<0)
      newCapacity=minCapacity;
   if (newCapacity - MAX_ARRAY_SIZE>0) //MAX_ARRAY_SIZE:值特別大,如果新的數組比它還大,就越界了,或者直接返回最大長度       newCapacity = hugeCapacity(minCapacity);
   elementData=Arrays. copyof(elementData, newCapacity);

 

 

    public void add(int index, E element) {
     //判斷下標是否越界 1rangeCheckForAdd(index);      //檢測是否需要擴容 ensureCapacityInternal(size
+ 1);
     //把index之後的所有元素向後移動一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
     
//
將index位置覆蓋上新值
elementData[index] = element; size++; }
private void 1rangeCheckForAdd(int index){
if(index>size || index<0)
throw new IndexOutofBoundsException(outofBoundsMsg(index));

 

System.arraycopy(elementData, index, elementData, index + 1,size - index); 舉例

int[] elementData = new int[10];
 for(int i=0;i<5;i++){ //數組的下標 0,1,2,3,4
elementData[i]=i+1;  //數組的值 1,2,3,4,5
System. out. println("=="+Arrays. toString(elementData)); 
int size=5; 
int index=1; //插入數組下標位置
 System. arraycopy(elementData, index, elementData, destPos: index+1, length: size-index);   原數組,插入指定的位置,又指定的數組,指定的位置為當前位置+1,到5-1=4的位置 >>從插入位置往後算
System. out. println("--"+Arrays. toString(elementData));

列印結果

 ==  [1,2,3,4,5,0,0,0,0,0]
 一  [1,2,2,3,4,5,0,0,0,0]  2:是要插入的數值

 

 

 向ArrayList添加對象時,原對象數目加1如果大於原底層數組長度,則以適當長度新建一個原數組的拷貝,並修改原數組,指向這個新建數組。

原數組自動拋棄(java垃圾回收機制會自動回收)。size則在向數組添加對象,自增1

 

 

刪除方法

指定位置刪除

public E remove(int index) {
     檢測當前下標是否越界 1rangeCheck(index); modCount
++; E oldValue = elementData(index);      將index+1及之後的元素向前移動一位,覆蓋被刪除的值 int numMoved = size - index - 1; if (numMoved > 0) 下下2System.arraycopy(elementData, index+1, elementData, index, numMoved);
    
將最後一個位置的元素清空
elementData[
--size] = null; return oldValue; }

 

private void 1rangeCheck(int index){
if(index >=size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

 

 

上上2 System.arraycopy(elementData, index+1, elementData, index, numMoved); 舉例int[] elementData = new int[10];

for(int i=0;i<10;i++){ //數組的下標 0,1,2,3,4,5,6,7,8,9
elementData[i]=i+1;  //數組的值 1,2,3,4,5,6,7,8,9,10
}
int size = elementData.lengh; //數組裡有幾個數,長度就是幾 elementData.lengh = 10
int index = 2;
int numMoved = size - index - 1; System. out. println("==" + Arrays. toString(elementData)); System. arraycopy(elementData, srcPos: index+1, elementData, index, numMoved); 原數組,指定的位置為要刪除的下標往後一個位置,又指定的數組,刪除位置的下標,10-2-1=7 >>從指定刪除下標位置往後數,全部向前移動一位
System. out. println("--" + Arrays. toString(elementData)); 列印結果 == [1,2,3,4,5,6,7,8,9,10] 3:要刪除的數字 一 [1,2,4,5,6,7,8,9,10,10]

 

 

 指定元素刪除

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    1fastRemove(index);
                    return true;} //從0開始遍歷,大小為數組長度,找到後直接刪除
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
     如果沒有匹配元素,返回false return false; }

快速刪除, 沒有檢測index下標的原因:因為之前需要指定下標刪除,現在是直接指定刪除某個元素,如果元素不存在,for語句遍歷也不會找到這個元素,所有,只要能滿足兩個if語句其中一個,那這個元素一定在我的合理範圍內的,所以就不需要檢測有沒有下標

 

 

快速刪除,沒有檢測index下標

private void 1fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

 ArrayList<Integer> list=new Arraylist();

1ist.add(10);
1ist.add(11);
1ist.add(12);
1ist.add(13);
1ist.add(15);
for(Integer num:1ist){   if(num==12){     1ist.remove(num);
  } }I //報錯 //Exception in thread"main"java.util.concurrentModificationException

final void checkForComodification){
  if(modcount != expectedModcount) //modcount:6 expectedModcount:5 >>
使用迭代器時,同步了一下 expectedModCount = modCount,迭代的時候一直在判斷,如果此時調用了 ArrayList.remove,modCount變化了,expectedModCount還是初始同步的值,也就throw new ConcurrentModificationException();
    throw new ConcurrentModificationException();
}

 

 

  modCount++ :  該欄位表示list結構上被修改的次數。在對集合進行新增或移除操作時會使modCount+1,這是一個記錄集合變化次數的變數。

 作用是在使用迭代器Iterator對集合進行遍歷時,用modCount來判斷集合內數據沒有發生變化,否則會拋出異常。

 

解決辦法:用迭代器刪除

//解決刪除異常問題
Iterator<Integer> it = list.iterator();
while(it.hasNext()){     //hasNext()方法 : 檢驗後面還有沒有元素。 從前往後查找。   Integer num=it.next(); //next()方法:獲取下一個元素
  if(
num == 12){
  //使用迭代器的刪除方法
  it.remove();
  }
}
public void remove(){   if(lastRet<0)     throw new I1legalstateException();
  checkForComodification();

  try{   ArrayList.this.remove(lastRet);
  cursor=lastRet;   1astRet=-1;
  //修改expectedModcound 和 modcount一樣
  expectedModcount = modcount; }catch(IndexoutofBoundsException ex){   throw new ConcurrentModificationException(); }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 今天講的是結構型設計模式中的最後一個,這個模式也就是代理模式,在前段時間我寫的一篇關於正向代理和反向代理的文章。雖說此代理非彼代理。但是代理一詞還是具有相似的含義的。這裡我們繼續使用文章中的代購一個例子來講述一下代理模式吧,人不方便去購買哪些物品,這時就有一個中間人,他來購買。他代替我去購買。 ...
  • 1.nfs實現的原理解析? 2.安裝、配置、nfs服務 1.安裝 2.配置 3.根據配置進行初始化環境 4.啟動 5.客戶端測試 6.錯誤的示範 7.多個客戶端共用一個存儲伺服器 (NFS) 8.實現開機自動掛載(因為伺服器不重啟) 擴展瞭解即可 3.nfs相關的配置參數 4.rw 和 ro 2.驗 ...
  • 如何界定爬蟲的合法性,我通過翻閱大量文章、事件、分享、司法案例,總結出界定的三個關鍵點 ...
  • 併發編程 併發(偽):由於執行速度特別快,人感覺不到 並行(真):創建10個人同時操作 線程 1. 單進程,單線程的應用程式 print('666') 2. 到底什麼是線程?什麼是進程 Python自己沒有這玩意,Python中調用的操作系統的線程和進程(偽線程) 3. 多線程 工作的最小單元 共用 ...
  • 一、線程替代方案 1.subprocess (1)完全跳過線程,使用進程 (2)是派生進程的主要替代方案 (3)python2.4後引入 2.multiprocessing (1)使用threading介面派生,使用子進程 (2)允許為多核或者多CPU派生進程,介面很threading非常相似 (3 ...
  • 上一篇博客已經給大家介紹了一些演算法題,明天剛好是中秋了,這裡祝大家中秋快樂。剛好趕上數學建模了,今天就先介紹與衡量演算法水平的重要指標時間複雜度吧。在時間充裕情況下會更新5+2。之後還會介紹空間複雜度以及python內置函數的時間複雜度。 1.簡介 先看一下什麼是時間複雜度: 衡量代碼的好壞,包括兩個 ...
  • 一、序列化組件 簡單使用 開發我們的Web API的第一件事是為我們的Web API提供一種將代碼片段實例序列化和反序列化為諸如 之類的表示形式的方式。我們可以通過聲明與Django forms非常相似的序列化器(serializers)來實現。 models部分: views部分: ModelSe ...
  • 之前通過hook技術實現了微信pc端發送消息功能,如果在結合圖靈機器人就能實現微信聊天機器人。 代碼下載:http://blog.yshizi.cn/131.html 邏輯如下: ![捕獲.jpg][1] 下麵我簡單介紹一下步驟。 1. 首先,你需要下載我的微信助手,下載地址請參考我的博客文章: [ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...