面試題:Java容器之ArrayList全解析

2021-03-02 Java技術前線

點擊上方「程式設計師面試圈」,選擇「置頂或者星標」

與你一起成長~

公眾號回復[ 資源 ] 領取888G學習資源

ArrayList結構圖

ArrayList 是 java 集合框架中比較常用的數據結構了。繼承自 AbstractList,實現了 List 接口。底層基於數組實現容量大小動態變化。允許 null 的存在。同時還實現了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速訪問、複製、序列化的。

ArrayList類簡介

1、ArrayList是內部是以動態數組的形式來存儲數據的、知道數組的可能會疑惑:數組不是定長的嗎?這裡的動態數組不是意味著去改變原有內部生成的數組的長度、而是保留原有數組的引用、將其指向新生成的數組對象、這樣會造成數組的長度可變的假象。

2、ArrayList具有數組所具有的特性、通過索引支持隨機訪問、所以通過隨機訪問ArrayList中的元素效率非常高、但是執行插入、刪除時效率比較地下、具體原因後面有分析。

3、ArrayList實現了AbstractList抽象類、List接口、所以其更具有了AbstractList和List的功能、前面我們知道AbstractList內部已經實現了獲取Iterator和ListIterator的方法、所以ArrayList只需關心對數組操作的方法的實現、

4、ArrayList實現了RandomAccess接口、此接口只有聲明、沒有方法體、表示ArrayList支持隨機訪問。

5、ArrayList實現了Cloneable接口、此接口只有聲明、沒有方法體、表示ArrayList支持克隆。

6、ArrayList實現了Serializable接口、此接口只有聲明、沒有方法體、表示ArrayList支持序列化、即可以將ArrayList以流的形式通過ObjectInputStream/ObjectOutputStream來寫/讀。

基礎屬性

ArrayList部分源碼如下:

public class ArrayList<E> extends AbstractList<E>

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

{

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private transient Object[] elementData;

private int size;

//...省略部分代碼

}

如上代碼中為ArrayList的主要屬性:

DEFAULT_CAPACITY:默認容量,即為初始值大小

EMPTY_ELEMENTDATA:共享的空數組,用於初始化空實例

elementData:ArrayList內部結構,是一個Object[]類型的數組

size:數組長度大小

構造方法

如下為ArrayList的構造方法:

1.public ArrayList(int initialCapacity)

2.public ArrayList()

3.public ArrayList(Collection<? extends E> c){

elementData = c.toArray();

size = elementData.length;

// c.toArray might (incorrectly) not return Object[] (see 6260652)

if (elementData.getClass() != Object[].class)

elementData = Arrays.copyOf(elementData, size, Object[].class);

}

1.構造方法1,表示接受指定地容量值,初始化創建數組,建議在可估算數組大小時,創建ArrayList可指定

2.構造方法2,是默認的構造方法,它將創建一個空數組

3.構造方法3,接收一個Collection的實體,將該Collection實體轉換為ArrayList對象

主幹流程

1.添加指定元素代碼如下

public boolean add(E e) {

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

可以看到實際上只有3行代碼,其流程主要如下:

1.擴容 (這裡便解釋了,在介紹時提出的問題):

主要源碼如下

private void ensureCapacityInternal(int minCapacity) {

if (elementData == EMPTY_ELEMENTDATA) {

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

//最大數組容量

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;

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

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

// minCapacity is usually close to size, so this is a win:

elementData = Arrays.copyOf(elementData, newCapacity);

}

1·擴容的大小為3/2倍原數組長度

2.若值newCapacity比傳入值minCapacity還要小,則使用傳入minCapacity,若newCapacity比設定的最大數組容量大,則使用最大整數值

3.實際擴容,使用了Arrays.copyof(elementData, newCapacity) (此處有兩個問題 1.為啥擴容是原來的3/2倍原數組的長度? 2.調用Arrays.copyOf(elementData, newCapacity)方法具體做了什麼操作? )

2.賦值:將添加的值放置到size++的位置上3.返回:返回true

2.添加指定元素到指定的位置上代碼如下:

public void add(int index, E element) {

rangeCheckForAdd(index);

ensureCapacityInternal(size + 1); // Increments modCount!!

System.arraycopy(elementData, index, elementData, index + 1,

size - index);

elementData[index] = element;

size++;

}

其流程為:

常見問題1.問題描述

在使用ArrayList比較常見的一個問題就是在遍歷ArrayList的時候調用remove()方法進行元素的刪除操作,從而得到意想不到的結果,本人在開發過程中也遇到過這樣的問題,所以在這裡提出了,希望能夠幫助到大家。

2.實例及分析

如下代碼中,在遍歷List時,調用了remove方法,刪除元素a

//arrayList中的值為 [a,a,c,a,a]

for (int i = 0; i < arrayList.size(); i++) {

if (arrayList.get(i) == "a") {

arrayList.remove(i);

}

}

System.out.println(arrayList);

這段代碼看似解決了刪除列表中所有的a元素,但是刪除後得出List的結果為[a, c, a],為什麼這種方式沒有達到想要的效果,其實仔細分析後會發現,在調用remove()方法時List的長度會發生變化而且元素的位置會發生移動,從而在遍歷時list實際上是變化的,例如

當i=0時,此時list中的元素為[a,a,c,a,a],

但當i=1時,此時List中的元素為[a,c,a,a],元素的位置發生了移動,從而導致在遍歷的過程中不能達到刪除的效果

3.解決方案

通過上述的分析可以看出,出現問題的原因是元素的位置發生了移動,從而導致異常的結果 方案一、逆向遍歷List刪除,代碼如下,這種做法可行主要是因為remove()方法刪除index處的元素時,是將index+1到size-1索引處的元素前移,而逆向遍歷可以避免元素位置的移動

for (int i = arrayList.size()-1; i >=0 ; i--) {

if (arrayList.get(i) == "a") {

arrayList.remove(i);

}

}

System.out.println(arrayList);

方案二、使用迭代器中的remove方法,迭代器具體參考Iterator詳解,主要代碼如下(這種方式比較推薦)

Iterator<String> ite = arrayList.listIterator();

while (ite.hasNext()){

if(ite.next() == "a")

ite.remove();

}

System.out.println(arrayList);

手寫一個ArrayList

自己手寫一個ArrayList,代碼如下:

public class MyArrayList<T> implements Iterable<T> {

private T[] theItems;

private int theSize;

private static final int DEAULT_CAPACITY=10;

public MyArrayList(){

theSize=0;

ensureCapacity(DEAULT_CAPACITY);

}

public void add(T data){

if(size()==theItems.length){

ensureCapacity(size()*2+1);

}

theItems[size()]=data;

theSize++;

}

public void add(int index,T data){

if(size()==theItems.length){

ensureCapacity(size()*2+1);

}

for(int i=theSize;i>index;i--){

theItems[i]=theItems[i-1];

}

theItems[index]=data;

theSize++;

}

public T get(int index){

if(index<0|index>=size()){

throw new IndexOutOfBoundsException("index error");

}

return theItems[index];

}

public T remove(int index){

T removeData=get(index);

for(int i=index;i<size()-1;i++){

theItems[i]=theItems[i+1];

}

theSize--;

return removeData;

}

public int size(){

return theSize;

}

private void ensureCapacity(int newCapacity){

if(theSize>newCapacity){

return;

}

T[] old=theItems;

theItems= (T[]) new Object[newCapacity];

for(int i=0;i<size();i++){

theItems[i]=old[i];

}

}

@Override

public Iterator<T> iterator() {

return null;

}

@Override

public void forEach(Consumer<? super T> action) {

}

@Override

public Spliterator<T> spliterator() {

return null;

}

}

總結

1.ArrayList是基於數組實現的,它的內存儲元素的數組為 elementData;elementData的聲明為:transient Object[] elementData;

2.ArrayList中EMPTYELEMENTDATA和DEFAULTCAPACITYEMPTY_ELEMENTDATA的使用;這兩個常量,使用場景不同。前者是用在用戶通過ArrayList(int initialCapacity)該構造方法直接指定初始容量為0時,後者是用戶直接使用無參構造創建ArrayList時。

3.ArrayList默認容量為10。調用無參構造新建一個ArrayList時,它的elementData = DEFAULTCAPACITYEMPTYELEMENTDATA, 當第一次使用 add() 添加元素時,ArrayList的容量會為 10。

4.ArrayList的擴容計算為 newCapacity = oldCapacity + (oldCapacity >> 1);且擴容並非是無限制的,有內存限制,虛擬機限制。

5.ArrayList的toArray()方法和subList()方法,在源數據和子數據之間的區別;

6.注意擴容方法ensureCapacityInternal()。ArrayList在每次增加元素(可能是1個,也可能是一組)時,都要調用該方法來確保足夠的容量。當容量不足以容納當前的元素個數時,就設置新的容量為舊的容量的1.5倍加1,如果設置後的新容量還不夠,則直接新容量設置為傳入的參數(也就是所需的容量),而後用Arrays.copyof()方法將元素拷貝到新的數組。從中可以看出,當容量不夠時,每次增加元素,都要將原來的元素拷貝到一個新的數組中,非常之耗時,也因此建議在事先能確定元素數量的情況下,才使用ArrayList,否則不建議使用。

參考資料

https://blog.csdn.net/crave_shy/article/details/17436773

https://www.jianshu.com/p/92373a603d42

https://juejin.im/post/5a1bc1006fb9a045030fce0e

                     每一個「好看」,都是對我們最大的肯定!

相關焦點

  • 經典Java面試題的答案——容器
    這是網際網路技術崗的分享專題,廢話少說,進入正題:18.Java 容器都有哪些?Java主要包括兩種類型的容器,一種是Collection,存儲一列元素,另一種是Map,存儲鍵/值對映射。Collection接口又有3種子類型,List、Set和Queue。19.Collection 和 Collections 有什麼區別?
  • 面試題:Java容器之LinkedList全解析
    點擊上方「程式設計師面試圈」,選擇「置頂或者星標」與你一起成長~
  • 面試題之java基礎
    應部分網友的建議,從今天起會逐步的總結一些java、php相關的面試題,由簡單到複雜歸納一個系列:【金三銀四】,中間部分題目的答案來源於網絡,如若不嚴謹還望諒解。java基礎面試題 1、簡述Java程序編譯和運行的過程:答:①  Java編譯程序將Java源程序翻譯為JVM可執行代碼--字節碼,創建完源文件之後,程序會先被編譯成 「.class」 文件。
  • Java 最常見的 200+ 面試題:面試必備
    聊回面試題這件事,這份面試清單原本是我們公司內部使用的,可到後來有很多朋友在微信上聯繫到我,讓我幫他們找一些面試方面的資料,而且這些關係也不太好拒絕,一呢,是因為這些找我,要面試題的人,不是我的好朋友的弟弟妹妹,就是我的弟弟妹妹們;二呢,我也不能馬馬虎虎的對付,受人之事忠人之命,我也不能辜負這份信任。
  • 2019 最新 200 道 Java 面試題
    ,我做了大量的「功課」,首先我研究了幾乎所有大廠的面試題,還和負責招聘工作的幾個朋友,詳細的探討了 Java 面試所要涉及的知識點,於是就有了今天大家看到的這 200 多道面試題。有了這些面試題不意味著,死記硬背之後就能進入企業工作,尤其是 BAT 等工作崗位競爭更為激烈,這些面試題只能成為面試體系中的一道「開胃菜」,從而提高了整個 Java 面試的壁壘,讓願與學的人,變的更加優秀,從而和懶惰的人拉開差距,讓企業也能更輕易的甄別。這些面試題包含哪些內容?
  • Java最常見600+面試題全解析:面試必備
    最近在刷面試題,所以需要看大量的Java相關的面試題,從大量的題目中總結了很多的知識,也分享給需要的同學。尚學堂與500+企業合作,建立IT行業最全的企業面試題庫。每周8~20家企業上門招聘,輕鬆掌握企業最新面試題集。本題集幾乎都是【必考題】,都能看懂的話,保你面試十拿九穩。
  • iOS 面試之道:117 道 iOS 面試題全解析
    因為這一系列的因素,促使我有強烈願望改變技術書出版流程和售賣方式,大致七八個月前道長和巧哥跟我聊到計劃從面試的角度寫一本系統的 iOS 技術書籍時候,我們一拍即合,有了今天的這次合作。好了,接下來該認真聊下今天的主角《iOS 面試之道》了。
  • 每日一課 | Java 中如何將 ArrayList 與 HashSet 互相轉換?
    將列錶轉換為集合Set set = new HashSet(list);將集轉換為列表List list = new ArrayList(set);1.列出示例import java.util.ArrayList;
  • Java容器--2021面試題系列教程(附答案解析)--大白話解讀--JavaPub版本
    Java容器--2021面試題系列教程(附答案解析)--大白話解讀--JavaPub版本前言序言再高大上的框架,也需要紮實的基礎才能玩轉,高頻面試問題更是基礎中的高頻實戰要點。適合閱讀人群Java 學習者和愛好者,有一定工作經驗的技術人,準面試官等。
  • 每周 10 道 Java 面試題 : 面向對象, 類加載器, JDBC, Spring 基礎概念
    (點擊上方公眾號,可快速關注)來源:lmportNew - 唐尤華每周10道 Java 面試題由 ImportNew
  • Java SSM框架相關基礎面試題整理
    一、Spring面試題1、Spring 在ssm中起什麼作用?
  • CopyOnWriteArrayList你都不知道,怎麼拿offer?
    大家對線程安全容器可能最熟悉的就是ConcurrentHashMap了,因為這個容器經常會在面試的時候考查。比如說,一個常見的面試場景:面試官問:「HashMap是線程安全的嗎?如果HashMap線程不安全的話,那有沒有安全的Map容器」3y:「線程安全的Map有兩個,一個是Hashtable,一個是ConcurrentHashMap」面試官繼續問:「那Hashtable和ConcurrentHashMap有什麼區別啊?」
  • Java面試總結之Java基礎
    無論是工作多年的高級開發人員還是剛入職場的新人,在換工作面試的過程中,Java基礎是必不可少的面試題之一。能不能順利通過面試,拿到自己理想的offer,在準備面試的過程中,Java基礎也是很關鍵的。對於工作多年的開發人員來說,Java基礎往往是會被大家所忽略的,但在面試的過程中,確是必不可少的問題。在這篇文章裡就來為大家總結一下經常會被問到的Java基礎題。
  • 這是我見過最有用的java面試題,面試了無數公司總結的
    Java 集合框架的面試題這部分也包含數據結構、算法及數組的面試問題38.List、Set、Map 和 Queue 之間的區別(答案)List 是一個有序集合,允許元素重複。它的某些實現可以提供基於下標值的常量訪問時間,但是這不是 List 接口保證的。Set 是一個無序集合。
  • Java面試題全集 第二彈(史上最強)
    面試題:2005年摩託羅拉的面試中曾經問過這麼一個問題「If a process reports a stack overflow run-time error, what’s the most possible cause?」
  • java面試——SpringMVC面試題集錦
    →[設為星標⭐]♪ 點擊上方綠標 收聽java面試——SpringMVC面試題集錦1、講下SpringMvc的核心入口類是什麼,Struts1,Struts2的分別是什麼SpringMvcinterceptor> <mvc:mapping path="/modelMap.do" /> <bean class="com.et.action.MyHandlerInterceptorAdapter" /></mvc:interceptor>20、講下SpringMvc的執行流程系統啟動的時候根據配置文件創建spring的容器
  • 滴滴Android崗面經分享:面試真題+經驗總結
    1.首先是自我介紹2.從筆試的算法題入手,詳細講講自己的思路。然後分析一下時間,空間複雜度提出優化和改進的方法3.再加一道手撕算法題4.考察了http,tcp等計算機網絡知識5.涉及了一小部分的作業系統6.常見的數據結構包括堆棧隊列等結構java中對應的類:從array,arrayList,linkedList,Queue, PriorityQueue,Deque, stack等等講了一遍
  • Java經典面試題Spring是什麼 Spring框架入門詳解
    下面請看java代碼我們通過ClassPathXmlApplicationContext類傳入applicationContext.xml配置文件的相對路徑,創建出spring的容器對象ApplicationContext,在通過容器對象中的方法獲取到Spring容器為我們創建的user對象,其實Spring兩個容器,除了
  • 面試前必看Java線程面試題
    下面是Java線程相關的熱門面試題,你可以用它來好好準備面試。1.面向對象的特徵有哪些方面?答:面向對象的特徵主要有以下幾個方面:- 抽象:抽象是將一類對象的共同特徵總結出來構造類的過程,包括數據抽象和行為抽象兩方面。
  • 要準備多少東西去面試---java中高級面試總結(值得收藏)
    、輸出java.util 集合類、時間處理模式、日期時間工具等各類常用工具包其它還有java.sql 訪問和處理來自於 Java 標準數據源數據的類java.test 以一種獨立於自然語言的方式處理文本、日期、數字和消息的類和接口java.math簡明的整數算術以及十進位算術的基本函數1.2. java