Java開發經常用到的ArrayList集合原理方法,這些你都清楚嗎

2020-09-30 16:00:51

前言

ArrayList 算是我們開發中最經常用到的一個集合了,使用起來很方便,對於內部元素的隨機存取很快。今天來分析下ArrayList的一些原理方法,對於平常工作和麵試都是非常有用的。

另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,我認為對面試來說是非常有用的,想要資料的話請點795983544 暗號CSDN。

在這裡插入圖片描述

一.ArrayList 簡介

ArrayList 是一個陣列佇列,相當於 動態陣列。與 Java 中的陣列相比,它的容量能動態增長。它繼承於 AbstractList,實現了 List, RandomAccess, Cloneable, java.io.Serializable 這些介面。 看過 ArrayList 原始碼的同學有沒有注意過有這麼一個細節:為什麼 ArrayList 實現了RandomAccess這個介面,但是 LinkedList 卻沒有實現這個介面?這是一個空介面,裡面沒有任何的方法,有什麼作用呢?
答案: RandomAccess 是一個標誌介面,表明實現這個這個介面的 List 集合是支援快速隨機存取的。也就是說,實現了這個介面的集合是支援 快速隨機存取 策略的。而 LinkedList 是不能實現隨機存取的。

二.ArrayList 資料結構

ArrayList 包含了兩個重要的物件:elementData 和 size。

  • elementData 是"Object[]型別的陣列",它儲存了新增到 ArrayList 中的元素。實際上,elementData
    是個動態陣列。

那是不是有人就會問既然 ArrayList 本質是陣列,那為啥它的長度可以改變?

首先,陣列的確長度不能改變。不過,ArrayList 內部有一系列騷操作,大概就是它每次覺得長度不夠就會 建立一個新陣列,這個新陣列的容量比原來多出 50%,把原來的陣列 copy 過來,然後把以前的陣列銷燬掉。

  • size 則是動態陣列的實際大小。

三. ArrayList 遍歷方式

  • 第 1 種,普通 for 迴圈隨機存取,通過索引值去遍歷。
// 隨機存取
     List<String> list = new ArrayList<>();
     int size = list.size();
     for (int i = 0; i < size; i++) {
         value = list.get(i);
     }
  • 第 2 種,通過迭代器遍歷。即通過 Iterator 去遍歷。
// 迭代器遍歷
    Iterator<String> iter = list.iterator();
    while (iter.hasNext()) {
        value = iter.next();
    }
  • 第 3 種,增強 for 迴圈遍歷。
 // 增強 for 迴圈
    for (String s : list) {
        value = s;
    }
  • 第 4 種 forEach + lambda 迴圈遍歷
list.forEach(p -> {
                p.hashCode();
            });

四種遍歷比較

  • 結論:如果資料量比較少的話貌似四種迴圈耗時都差不多,但是隨著資料量的增長會發現 foreach 的效率是最好的。但是從上面我們會發現一個奇怪的現象,第一次迴圈的時候forEach遍歷的時間是最長的儘管資料量非常少也會這樣。但是後面的耗時就正常了。如果放開測試裡面的預熱程式碼,每次跑出來的耗時也是正常的。

四.ArrayList 刪除資料

雖然有四種遍歷方式,但是能夠正確刪除資料的方式只有兩種

  • 第 1 種通過迭代器進行刪除。這種方式的話,也是《阿里程式碼規約》所推薦的。

在這裡插入圖片描述

Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
            iter.next().hashCode();
            iter.remove();
        }
  • 第 2 種倒序迴圈刪除
for(int i = list.size()-1;i>=0;i--){
            list.remove(i);
        }

下面再演示下錯誤的刪除操作

  • 普通 for 迴圈正序刪除,刪除過程中元素向左移動,不能刪除重複的元素
 List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        for(int i=0;i<list.size();i++){
           list.remove(i);
        }
        System.out.println(String.join(",",list));

結果輸出:1

  • 增強 for 迴圈刪除會丟擲 java.util.ConcurrentModificationException

五.ArryList 注意點

謹慎使用 ArrayList 中的 subList 方法

  • ArrayList 的 subList 結果不可強轉成 ArrayList,否則會丟擲 ClassCastException 異常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 說明:subList 返回的是 ArrayList 的內部類 SubList,並不是 ArrayList ,而是 ArrayList 的一個檢視,對於 SubList 子列表的所有操作最終會反映到原列表上。
List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        ArrayList<String> strings =  (ArrayList)list.subList(0, 1);

執行結果:
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
  at com.workit.demo.listener.ArrayListTest.main(ArrayListTest.java:29)
  • 在 subList 場景中,高度注意對原集合元素個數的修改,會導致子列表的遍歷、增加、 刪除均會產 ConcurrentModificationException 異常。
 List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        List<String> subList =  list.subList(0, 1);
        // 對原 List 增加一個值
        list.add("10");
        subList.add("11"); // 這一行會報 java.util.ConcurrentModificationException
  • 初始化 List 的時候儘量指定它的容量大小。(儘量減少擴容次數)

結束語

  • 由於自己才疏學淺,難免會有紕漏,假如你發現了錯誤的地方,還望留言給我指出來,我會對其加以修正。
  • 如果你覺得文章還不錯,你的轉發、分享、讚賞、點贊、留言就是對我最大的鼓勵。
  • 感謝您的閱讀,十分歡迎並感謝您的關注。

另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,我認為對面試來說是非常有用的,想要資料的話請點795983544 暗號CSDN。

在這裡插入圖片描述

在這裡插入圖片描述