金三銀四跳槽季,BAT美團滴滴java面試大綱(帶答案版)之一:Java基礎篇

Java基礎篇:

題記:本系列文章,會盡可能模擬面試現場對話情景, 用口語而非書面語 ,採用問答形式來展示。另外每個問題都附上「延伸」,這部份內容是幫助小夥伴們更深的理解一些底層細節的補充,在面試中可能不多直接涉及,權當是提升自身水平的知識儲備吧。java

第一部分:java容器相關

1.問:List 和 Set 都有什麼區別?面試

分析:這種問題面試官通常想考察的都是你對這兩種數據結構的瞭解,已經使用時候的選擇依據,因此能夠從數據結構和一些使用案例入手分別作個介紹數組

答:List,列表,元素可重複。經常使用的實現ArrayList和LinkedList,前者是數組方式來實現,後者是經過鏈表來實現,在使用選擇的時候,通常考慮的是基本數據結構的特性,好比,數組讀取效率較高,鏈表插入時效率較高。安全

Set,集合,特色就是存儲的元素不可重複。經常使用是實現,是HashSet和TreeSet,分開來談:數據結構

    HashSet,底層實現是HashMap,存儲時,把值存在key,而value統一存儲一個object對象。排重的時候,是先經過對象的hashcode來判斷,若是不相等,直接存儲。若是相等,會再對key作equals判斷,若是依然相等,不存儲,若是不相等,則存入,咱們知道,HashMap是數組+鏈表的基本結構,一樣的,在HashSet中,也是經過一樣的策略,存儲在相同的數組位置下的鏈表中。多線程

    TreeSet,存入自定義對象時,對象須要實現Comparable接口,重寫排序規則。使用場景通常是須要保證存儲數據的有序和惟一性。底層數據結構是自平衡的二叉排序樹(紅黑樹)併發

延伸:app

    a.在說到HashMap時,可能會直接引到HashMap相關話題,我發現這個問題面試官很是喜歡問,多是由於HashMap可聊的較多,也能很好的考驗下應聘者對底層實現細節、源碼閱讀、刨根問底的態度。高併發

    b.涉及到二叉樹了,小端就遇到過一次問紅黑樹特性的,由於以前準備過,成竹在胸的啪啪啪正要一一道來呢,結果剛說到第二個特性,面試官就問:紅黑樹和普通的平衡二叉樹有什麼區別?當時一臉懵逼樣...回來後趕忙補足,核心區別就是:紅黑樹也是二叉查找樹的一種,二叉樹須要經過自旋、或其餘方式(好比紅黑樹還能經過變色)來保證平衡(不然就成了鏈表結構了,沒有時間複雜度上的優點了),紅黑樹限定的條件相對來講比較寬鬆,也就是說在平衡的過程當中,消耗相對較小。線程

  c.因爲HashSet無序,爲了實現有序的目的,又不想用其餘數據結構,能夠用LinkedHashSet。簡要說明,同HashSet和HashMap關係同樣,也是使用了一個LinkedHashMap,LinkedHashMap和普通的HashMap的區別就是,在原有數據結構之上,採用雙向鏈表的形式將全部entry(注意,是前面講過的數組+鏈表中的各個鏈表裏的元素,作了鏈接)連起來,順序就是entry的插入順序,這樣能夠保證元素的迭代順序和插入順序相同(有序性)

2.HashMap 是線程安全的嗎,爲何不是線程安全的?

分析:這種問題,既然面試官問起,確定是在這方面有的聊的。這個問題,在多數狀況下,能清晰、全面的描述出問題前因後果便可,不多有面試官真的拿一段源碼來考。固然,做爲應聘者,若是能理解的很透徹,再用簡單的圖邊寫邊講,做爲補充,仍是很是出彩的。

答:嗯,不是線程安全的。主要從兩個方面來講:

        a.在插入新數據的時候,多線程hash後的結果相同,插入位置也就會定位到數組的相同下標下的同一個鏈表中。在插入時,假如第二個線程在第一個線程插入的瞬間也插入,就可能會致使,覆蓋前面插入的值。

        b.第二個非線程安全的影響是在擴容的時候,擴容會把全部值從新hash,插入到新的擴容後的「數組+鏈表」結構中。可能會致使環狀鏈表狀況出現,這樣在讀取節點的next節點時,永不爲空,也就是著名的擴容時CPU使用率100%狀況。

 延伸:

要作到心中有底,仍是須要知其然知其因此然才行,因此,擼下源碼好好想一想,才能作到臨陣不亂。

        a.建議好好看看源碼,源碼量不大,結構也比較清晰。

        b.針對環狀鏈表的狀況,我是看了別人寫的圖文並茂版的文章弄明白的,這裏放上連接,供參考:HashMap高併發問題

3.問:那你是用什麼數據結構來做爲替代,知足線程安全的場景要求呢?

分析:這裏通常面試官想考察的是對ConcurrentHashMap的瞭解,可是也有個別狀況,會涉及到HashTable,小端就吃過這方面的虧,明明能夠一筆帶過的小知識點,卻因爲準備不充分,而沒能答完整。

答:在Java裏,提供瞭如下數據結構,能夠解決線程安全問題:

a.HashTable,實現原理是經過Synchronize同步鎖來把讀寫方法都加鎖的方式。雖然線程安全了,可是效率低下,只要有讀寫,就不能作其餘操做。

b.SynchronizedMap(涉及較少,瞭解便可),有條件的同步,是Collections類提供的一個方法返回的HashMap的多線程版本。實現是把基本的方法都加了同步鎖。

c.ConcurrentHashMap(重頭戲):根據jdk版本不一樣,實現也有差異。

java8之前,是用segment(鎖住map一段實現,默認是16段也就是能夠併發數支持到16,也可自定義。讀不受影響),數據結構爲數組(segment)+數組(entry)+鏈表,適用於讀多寫少的場景。提供原子操做putIfAbsent()(沒有則添加)。segment繼承自ReentrantLock,來做爲鎖。

java8,元素結構爲Node(實現Entry接口),數據結構爲數組+鏈表;直接對Node進行加鎖,粒度更小。當鏈表長度大於8,轉換爲紅黑樹,固然在轉換前,先看下數組長度,若是小於64,那先經過擴容來實現;插入元素時,若是該位置爲null,用CAS插入;若是不爲null,則加Synchronize鎖插入到鏈表;

擴展:

        a.咱們看到,數組的默認長度都是16,那麼這個數字有什麼深意嗎?有!作運算時效率高!屬於取巧的設計。一個是擴容的時候,直接向左位操做,移一位,擴張爲二倍;二是hash取模的時候,hash值是32位,右移28位,剩高四位,而後與這個length-1也就是15(1111)按位與操做,使數據均勻分佈。

        b.ConcurrentHashMap在獲取size時候是怎麼計算的呢?

1.7size(),先獲取segment的大小,而後判斷是否修改過,若是是,在加鎖從新獲取segment大小,而後把全部segment大小加在一塊兒;

1.8size()的實現是用一個volatile 變量來作CAS修改,若是高併發,還會把部分值存到一個volatile數組。取值時,把這兩部分的值加在一塊兒。mappingcount()方法和size()方法實現方式同樣

        c.hashmap可使用null做爲key和value,存儲於table數組第一個節點;hashtable不容許key和value爲null.(這個在一個小公司被面試官問倒了,汗顏)

        d.瞭解一些其餘的數據結構:

ConcurrentSkipListMap 數據有序

ConcurrentSkipListSet 能去重

        e.hashset是用hashmap實現,內部存的是key,全部的value都是同一個object

好,這篇就先寫到這,你們還有什麼想看的,在下面留言告訴我,我會優先整理出來哦。

 

     (期待你的留言交流!點下"推薦",手留餘香,謝謝謝謝)

    (掃描下方二維碼,關注我的公衆號,第一時間看到你想要的更多專業文章)

 

        

相關文章
相關標籤/搜索