對線面試官 | 別再問我Java List八股文了

程式設計師大彬發表於2022-02-13

本期是【對線面試官】系列文章的第2期,主要是Java List高頻面試題目。

回覆【面試】獲取大彬精心整理的大廠面試手冊,已助力多位小夥伴斬獲大廠offer!

面試現場

面試官:你好,我是面試官xxx,請問你是大彬嗎?

大彬:面試官,您好,我是大彬

面試官:現在方便麵試嗎?

大彬:嗯嗯,可以的

面試官:那我們現在開始面試吧

面試官看你簡歷上寫了熟悉集合相關內容,你瞭解Java的List嗎?

大彬:嗯,List是一個介面,常見的實現類有ArrayList和LinkedList

面試官講講這兩個實現類的區別?

獨白:老八股文了哈哈

大彬:ArrayList的底層資料結構是陣列,支援下標訪問,查詢資料快。預設初始值大小為10,容量不足時會進行擴容

大彬:而LinkedList的底層資料結構是連結串列,將元素新增到連結串列的末尾,無需擴容

面試官嗯嗯,剛剛你提到ArrayList的擴容,詳細講講?

獨白:好傢伙,就知道你會問這個,八股文都已經準備好了。

大彬:ArrayList擴容原始碼實現如下:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  //擴容
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_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 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);
}

大彬:可以看到,在grow方法裡面進行擴容,將陣列容量擴大為原來的1.5倍。

大彬:舉個例子,如果初始化的值是8,當新增第9個元素的時候,發現陣列空間不夠,就會進行擴容,擴容之後容量為12。

大彬:擴容之後,會呼叫Arrays.copyOf()方法對陣列進行拷貝。

面試官嗯,不錯,那ArrayList和LinkedList分別適用於什麼場景?

大彬:對於隨機index訪問的get和set方法,ArrayList的速度要優於LinkedList。因為ArrayList直接通過陣列下標直接找到元素;LinkedList要移動指標遍歷每個元素直到找到為止。

大彬:新增和刪除元素,LinkedList的速度要優於ArrayList。因為ArrayList在新增和刪除元素時,可能擴容和複製陣列;而LinkedList的新增和刪除操作只需要修改指標即可。

大彬:因此,ArrayList適用於查詢多,增刪少的場景。而LinkedList適用於查詢少,增刪多的場景

面試官講講Set和List的區別?

大彬:List 以索引來存取元素,有序的,元素是允許重複的,可以插入多個null;Set 不能存放重複元素,無序的,只允許插入一個null。

大彬:List 底層實現有陣列、連結串列兩種方式;Set 基於 Map 實現,Set 裡的元素值就是 Map的鍵值。

面試官瞭解Vector嗎?

大彬:嗯,Vector是底層結構是陣列,現在基本沒有使用Vector了,因為操作Vector效率比較低。相對於ArrayList,它是執行緒安全的,在擴容的時候容量擴充套件為原來的2倍。

面試官嗯,那你還知道有哪些執行緒安全的List嗎?

大彬:可以使用Collections.synchronizedList()方法返回一個執行緒安全的List。

大彬:還有另一種方式,使用CopyOnWriteArrayList。

面試官嗯哼,剛剛你提到CopyOnWriteArrayList,詳細講講它的原理?

獨白:這也太捲了吧,一言不合就是底層原理...

大彬:CopyOnWriteArrayList是一個執行緒安全的List,底層是通過複製陣列的方式來實現的。

大彬:所謂的CopyOnWrite,就是寫時複製。

大彬:當我們往容器新增元素時,不直接往容器新增,而是先將當前容器進行復制,複製出一個新的容器,然後往新的容器新增元素,新增完元素之後,再將原容器的引用指向新容器。

大彬:這樣做的好處就是可以對CopyOnWrite容器進行併發的讀而不需要加鎖,因為當前容器不會被修改。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //add方法需要加鎖
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1); //複製新陣列
        newElements[len] = e;
        setArray(newElements); //原容器的引用指向新容器
        return true;
    } finally {
        lock.unlock();
    }
}

面試官那你能說說CopyOnWriteArrayList有什麼缺點嗎?

大彬:主要有以下兩個問題。

大彬:記憶體佔用問題。由於CopyOnWrite的寫時複製機制,在進行寫操作的時候,記憶體裡會同時駐紮兩個物件的記憶體。

大彬:CopyOnWrite容器不能保證資料的實時一致性,可能讀取到舊資料。

面試官:嗯,可以。

面試官問一些平時開發經常遇到的問題,怎麼給List排序呢?

大彬:可以使用list自身的sort方法,或者使用Collections.sort(list)方法。

面試官怎麼在遍歷 ArrayList 時移除一個元素?

大彬:如果使用foreach刪除元素的話,會導致快速失敗(fast-fail)問題,可以使用迭代器的 remove() 方法,避免fast-fail問題。

Iterator itr = list.iterator();
while(itr.hasNext()) {
      if(itr.next().equals("dabin") {
        itr.remove();
      }
}

面試官:基礎還不錯!回去等通知

大彬:好的,謝謝你

獨白:回去等通知?不會涼了吧...

相關文章