迭代器筆試題,看看你會不會?
導讀 | 有位小朋友最近正在為年後換工作做準備,但是遇到一個問題,覺得很不可思議的一道筆試題。然後我把這道題發到技術群裡,發現很多人居然不知道,很多都是連蒙帶猜的說。感覺很有必要寫一篇文章來說道說道。 |
有位小朋友最近正在為年後換工作做準備,但是遇到一個問題,覺得很不可思議的一道筆試題。然後我把這道題發到技術群裡,發現很多人居然不知道,很多都是連蒙帶猜的說。感覺很有必要寫一篇文章來說道說道。
奇怪的筆試題閱讀下面這段程式碼,請寫出這段程式碼的輸出內容:
import java.util.ArrayList; import java.util.Iterator; import java.util.*; public class Test { public static void main(String[] args) { Listlist = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); Iterator iterator = list.iterator(); while (iterator.hasNext()) { String str = (String) iterator.next(); if (str.equals("2")) { iterator.remove(); } } while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("4"); } }
他寫出來的答案是:
1 3 4
奇怪的是,你把這道題目發給你身邊人,讓他們回答這道面試題輸出結果是什麼,說這個結果的人非常多。不行你試試。
答案明顯不對,因為在第一個while裡的 iterator.hasNext()==false後才會到第二個while裡來,同一個Iterator物件,前面調一次iterator.hasNext()==false,再判斷一次結果不還是一樣嗎?,
所以第二個while判斷為false,也就不會再去遍歷iterator了,由此可知本體答案是:4。
下面我們來分析一下為什麼是具體底層是怎麼實現的。
這裡的Iterator是什麼?
迭代器是一種模式、詳細可見其設計模式,可以使得序列型別的資料結構的遍歷行為與被遍歷的物件分離,即我們無需關心該序列的底層結構是什麼樣子的。只要拿到這個物件,使用迭代器就可以遍歷這個物件的內部
Iterable 實現這個介面的集合物件支援迭代,是可以迭代的。實現了這個可以配合foreach使用~
Iterator 迭代器,提供迭代機制的物件,具體如何迭代是這個Iterator介面規範的。
Iterator說明
public interface Iterator{ //每次next之前,先呼叫此方法探測是否迭代到終點 boolean hasNext(); //返回當前迭代元素 ,同時,迭代遊標後移 E next(); /*刪除最近一次已近迭代出出去的那個元素。 只有當next執行完後,才能呼叫remove函式。 比如你要刪除第一個元素,不能直接呼叫 remove() 而要先next一下( ); 在沒有先呼叫next 就呼叫remove方法是會丟擲異常的。 這個和MySQL中的ResultSet很類似 */ default void remove() { throw new UnsupportedOperationException("remove"); } default void forEachRemaining(Consumer action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }
這裡的實現類是ArrayList的內部類Itr。
private class Itr implements Iterator{ int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such //modCountshi ArrayList中的屬性,當新增或刪除的時候moCount值會增加或者減少 //這裡主要是給fail-fast使用,避免一遍在遍歷,一遍正在修改導致資料出錯 //此列表在結構上被修改的次數。結構修改是指改變結構尺寸的修改列表, //或者以這樣的方式對其進行擾動,進步可能會產生錯誤的結果。 int expectedModCount = modCount; public boolean hasNext() { //cursor初始值為0,沒掉一次next方法就+1 //size是ArrayList的大小 return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); //把ArrayList中的陣列賦給elementData Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); //每呼叫一次next方法,遊標就加1 //cursor=lastRet+1 cursor = i + 1; //返回ArrayList中的元素 return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { //呼叫ArrayList中remove方法,溢位該元素 ArrayList.this.remove(lastRet); //cursor=lastRet+1, //所以此時相當於cursor=cursor-1 cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
第一個iterator.hasNext()
hasNext方法中:cursor==0, size==3,所以cursor != size返回true。
next方法中:cursor=0+1。返回"1"。
hasNext方法中:cursor==1, size==3,所以cursor != size返回true。
next方法中:cursor=1+1。返回"2"。
remove方法中:cursor==cursor-1==2-1=1,把ArrayList中的"2"給刪除了,所以size==2。
hasNext方法中:cursor==1, size==2,那麼cursor != size返回true。
next方法中:cursor=1+1==2;返回"3"。
hasNext方法中:cursor==2, size==2,那麼cursor != size返回false。
第二個iterator.hasNext()
hasNext方法中:cursor==2, size==2,所以cursor != size返回false。
所以,最後只輸出"4",即答案為4.
Iterator對集合類中的任何一個實現類,都可以返回這樣一個Iterator物件。可以適用於任何一個類。
因為集合類(List和Set等)可以裝入的物件的型別是不確定的,從集合中取出時都是Object型別,用時都需要進行強制轉化,這樣會很麻煩,用上泛型,就是提前告訴集合確定要裝入集合的型別,這樣就可以直接使用而不用顯示型別轉換.非常方便.
for each以用來處理集合中的每個元素而不用考慮集合定下標。就是為了讓用Iterator簡單。但是刪除的時候,區別就是在remove,迴圈中呼叫集合remove會導致原集合變化導致錯誤,而應該用迭代器的remove方法。
採用ArrayList對隨機訪問比較快,而for迴圈中的get()方法,採用的即是隨機訪問的方法,因此在ArrayList裡,for迴圈較快
採用LinkedList則是順序訪問比較快,iterator中的next()方法,採用的即是順序訪問的方法,因此在LinkedList裡,使用iterator較快
從資料結構角度分析,for迴圈適合訪問順序結構,可以根據下標快速獲取指定元素.而Iterator 適合訪問鏈式結構,因為迭代器是透過next()和Pre()來定位的.可以訪問沒有順序的集合.
而使用 Iterator 的好處在於可以使用相同方式去遍歷集合中元素,而不用考慮集合類的內部實現(只要它實現了 java.lang.Iterable 介面),如果使用 Iterator 來遍歷集合中元素,一旦不再使用 List 轉而使用 Set 來組織資料,那遍歷元素的程式碼不用做任何修改,如果使用 for 來遍歷,那所有遍歷此集合的演算法都得做相應調整,因為List有序,Set無序,結構不同,他們的訪問演算法也不一樣.(還是說明了一點遍歷和集合本身分離了)。
迭代出來的元素都是原來集合元素的複製。
Java集合中儲存的元素實質是物件的引用,而非物件本身。
迭代出的物件也是引用的複製,結果還是引用。那麼如果集合中儲存的元素是可變型別的,那麼可以透過迭代出的元素修改原集合中的物件。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2757310/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 筆試不會的東西筆試
- 命令列不會?看這裡命令列
- 【學習筆記】不會吧不會吧,不會有人還在手寫堆吧筆記
- 面試中常見的幾道智力題 來看看你會做幾道(2)?面試
- web前端工程師面試題10條必會筆試題Web前端工程師面試題筆試
- 百度大牛總結十條Python面試題陷阱,看看你是否會中招Python面試題
- 技術面試中常見的幾道智力題 來看看你會做幾道?面試
- 一道被前端忽略的基礎題,不信看你會幾題前端
- 還不會正規表示式?看這篇!
- 不會DRF?原始碼都分析透了確定不來看?原始碼
- 面試問到springmvc不會怎麼辦?看這篇就夠了,springmvc面試SpringMVC
- 不知道面試會不會問Lambda怎麼用面試
- 面試官問我會不會Elasticsearch,我語塞了...面試Elasticsearch
- 都9012年了,怎麼還會有人不會面試?面試
- 速看!這8道嵌入式面試題你都會嗎?面試題
- 幽默:會提問題比會答題考試更重要
- 這些瀏覽器面試題,看看你能回答幾個?瀏覽器面試題
- Leetcode 記錄 不會解的題LeetCode
- 面試官:物件可能會遲到,但它永遠不會缺席面試物件
- SDK怎麼測試?俺不會啊
- 竟然不會函式節流跟防抖,來看這篇包學包會函式
- 不會git的程式設計師,會不會被鄙視?Git程式設計師
- 測試工程師看過來!面試,你真的會嗎?工程師面試
- Windows10系統開機登入會解除嗎?不會可以這樣試試Windows
- 你應該會的一道多執行緒筆試題執行緒筆試
- C++中迭代器筆記C++筆記
- Promise不會??看這裡!!!史上最通俗易懂的Promise!!!Promise
- 不會用SpringBoot連線Redis,那就趕緊看這篇Spring BootRedis
- 15 年測試經驗,卷不動了,把機會留給後浪們吧,出去讀書試試看~
- 面試官問:Kafka 會不會丟訊息?怎麼處理的?面試Kafka
- 10道機器學習、深度學習必會面試題機器學習深度學習面試題
- 機器學習面試題,更有大廠內推機會機器學習面試題
- 寫給那些會做不會說的測試員!“它”正在摧毀你的面試……面試
- securecrt保持會話不會斷掉Securecrt會話
- Android面試題整理,Android開發者應該會哪些東西才不會被公司淘汰?內含福利Android面試題
- 在ArrayList的迴圈中刪除元素,會不會出現問題?
- 2019年常見的Linux面試題及答案解析,哪些你還不會?Linux面試題
- 不喜歡 IDE?試試看 grepgitviIDEGit