
收藏
84. 告訴我們關於迭代器及其使用方法
集合是所有 Java 開發人員面試中最喜歡的話題。當回答有關集合層次結構的問題時,考生經常說它是從集合介面開始的。但事實並非如此。上面還有另一個介面:Iterable。此介面由iterator()方法組成,該方法允許您存取目前集合的Iterator物件。這個Iterator物件到底是什麼?Iterator物件提供了在集合中移動並迭代其元素的能力,並且使用者不需要知道集合的具體實作細節。換句話說,它是一種指向集合元素的指針,就好像它正在窺視其中一個元素一樣。迭代器具有以下方法:-
hasNext() —如果迭代有另一個元素,則傳回true (此方法讓您知道何時到達集合的末端);
-
next() — 傳回迭代中的下一項。如果沒有,則拋出NoSuchElementException 。這意味著在使用此方法之前,最好使用hasNext()方法來確保下一個元素存在;
-
remove() — 從集合中刪除使用next()方法接收的最後一個元素。如果next()從未被呼叫過,那麼呼叫remove()將導致拋出IllegalStateException ;
-
forEachRemaining(<Consumer>) — 對集合的每個元素執行傳遞的操作(此方法出現在 Java 8 中)。
List<String> list = new ArrayList<>();
list.add("Hello ");
list.add("World, ");
list.add("It's ");
list.add("Amigo!");
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
iterator.next();
iterator.remove();
}
System.out.println(list.size());
控制台將顯示以下內容:
iterator.forEachRemaining(x -> System.out.print(x));
但是一旦我們這樣做了,迭代器就不再適合進一步使用:它已經遍歷了整個列表,而普通的迭代器沒有向後迭代的方法。這使得我們可以很好地討論LinkedList,特別是它的listIterator()方法,該方法傳回增強類型的迭代器:ListIterator。除了常規(標準)迭代器的方法之外,這種迭代器還具有以下功能:
-
add(<Element>) — 將新元素加入到清單中;
-
hasPrevious() —如果有一個元素位於下一個元素之前(如果有前一個元素),則傳回true ;
-
nextIndex() — 傳回下一個元素的索引;
-
previous() — 傳回上一個元素(下一個元素之前的元素);
-
previousIndex傳回前一個元素的索引。
-
set(<Element>) — 替換next()或previous()傳回的最後一個元素。

85. Java 集合框架中存在什麼集合層次結構?
Java 中有兩種集合層次結構。 第一個層次結構是 Collection 層次結構,其結構如下:
-
集合是描述集合的接口,集合是包含無序唯一(非重複)元素的資料結構。此介面有一些標準實作:TreeSet、HashSet和LinkedHashSet。
-
列表是一個描述儲存有序物件序列的資料結構的介面。清單中的物件可以透過清單中的索引插入和刪除(類似於數組,但可以動態調整大小)。這個介面還有一些標準實作:ArrayList、Vector(已棄用且未實際使用)和LinkedList。
-
佇列是一個接口,描述將專案儲存在先進先出 (FIFO)佇列中的資料結構。這個介面有以下標準實作:LinkedList(沒錯,它也實作了Queue)和PriotityQueue。


86.ArrayList的內部結構是什麼?
ArrayList就像一個數組,但它可以動態擴展。這意味著什麼?在底層,ArrayList使用普通數組,即將其元素儲存在預設大小為 10 個單元格的內部數組中。一旦內部數組滿了,就會建立一個新數組。新數組的大小由以下公式決定:<size of the current array> * 3 / 2 + 1
因此,如果數組的大小為 10,則新數組的大小將為:10 * 3 / 2 + 1 = 16。然後使用內建函數將原始(舊)數組中的所有值複製到其中System.arraycopy( )方法,原數組被刪除。簡而言之,這就是ArrayList實現動態調整大小的方式。讓我們考慮一下最受歡迎的ArrayList方法: 1. add(<Element>) — 在先檢查陣列中是否有可用單元之後,將一個元素新增至陣列結尾(最後一個空單元)。如果沒有,則建立一個新數組,並將元素複製到其中。此操作的時間複雜度為O(1)。還有一個類似的add(<Index>, <Elelement>)方法。它不會將元素添加到列表(數組)的末尾,而是添加到作為參數傳入的索引指示的特定單元格中。在這種情況下,時間複雜度將根據您添加的位置而有所不同:
- 如果新增接近清單的開頭,那麼時間複雜度將接近 O(N),因為位於新元素右側的所有元素都必須向右移動一個儲存格;
- 如果將元素插入到中間,那麼它將是 O(N/2),因為我們只需要將一半的清單項目向右移動一個儲存格。
87. LinkedList的內部結構是什麼?
ArrayList包含內部陣列中的元素,而LinkedList將它們儲存在雙向鍊錶中。這意味著每個元素都包含到前一個元素和下一個元素的連結。第一個元素不連結到前一個元素(畢竟,它是第一個)。它也被認為是列表的頭部,並且LinkedList物件可以直接引用它。同樣,最後一個元素沒有下一個元素,因為它是列表的尾部。LinkedList物件也直接引用它。這意味著存取清單的頭或尾的時間複雜度是 O(1)。 在ArrayList中,如果清單成長,則內部陣列也會成長。在這裡一切都變得更容易:添加引用就像更改幾個連結一樣簡單。讓我們來看看一些最常用的LinkedList方法: 1. add(<Element>) — 將一個元素加入到列表末尾,即在最後一個元素 (5) 之後,將新增指向新元素的連結作為下一個元素。新元素中的前一個引用將指向列表中現在位於其先前的元素 (5)。此操作的時間複雜度為 O(1),因為我們只需要連結到最後一個元素,並且您會記得,LinkedList 物件具有對尾部的直接引用,並且存取它具有最小的常數時間複雜度。2. add(<Index>, <Element>) — 依索引新增元素。假設在清單中間新增元素時,此方法首先從頭部和尾部(兩個方向)迭代元素,直到找到所需的位置。如果我們在第三個和第四個元素之間添加一個元素(如上圖所示),那麼第三個元素的下一個連結將指向新元素。新加入的元素的前一個將指向第三個。反過來,舊的第四個元素的前一個連結現在將指向新元素,新元素的下一個連結將指向新的第四個元素: 此方法的時間複雜度取決於新元素的索引:

- 如果它靠近頭部或尾部,則操作將接近 O(1),因為實際上不需要迭代元素;
- 如果靠近中間,那麼我們的時間複雜度為 O(N/2),因為方法將同時從頭部和尾部搜索,直到找到所需的元素。

88. HashMap的內部結構是什麼?
這可能是 Java 開發人員候選人最常見的面試問題之一。HashMap使用鍵值對。它們如何儲存在HashMap本身中?HashMap有一個內部節點數組:Node<K,V>[] table
預設情況下,數組的大小為 16,並且每次填充元素時都會加倍(即,當達到LOAD_FACTOR時;它指定數組可以達到的滿閾值 - 預設為0.75) 。每個節點都儲存鍵的雜湊值、鍵、值以及對下一個元素的引用:在 
- 該單元格是空的——在這種情況下,新的節點值儲存在其中。
- 單元格不為空-在這種情況下,將比較鍵的值。如果相等,則新的Node值會覆寫舊的 Node 值;如果不相等,則訪問下一個,並比較它的鍵...依此類推,直到新值覆蓋某些舊值,或到達單鍊錶的末尾,然後將新值存儲在那裡作為最後一個元素。

GO TO FULL VERSION