集合三兄弟List,Set,Map傻傻理不清?掌握訣竅面面俱到!

2020-10-23 14:00:27

前言:

作為Java基礎知識的核心部分,集合方面是面試時的重中之重,List、Set、map等相信大家都不會陌生,當然面試官也不會從簡單的問題出發,因為他也已經問吐了,今天就聊一下集合在面試中的高階部分,別再傻傻分不清了!
在這裡插入圖片描述

一、List、Map、Set三個介面,存取元素時,各有什麼特點?

(1)Set集合的add有一個boolean型別的返回值,當集合中沒有某個元素時,則可以成功加入該 元素,返回結果為true;當集合中存在與某個元素equals方法相等 的元素時,則無法加入該元素, 取元素時只能用Iterator介面取得所有元素,在逐一遍歷各個元素;

(2)List表示有先後順序的集合,呼叫add()方法,指定當前物件在集合中的存放位置;一個物件可 以被反覆存進集合中;每呼叫一次add()方法,該物件就會被插入集合中一次,其實,並不是把對 象本身存進了集合中,而是在集合中使用一個索引變數指向了該物件,當一個物件被add多次時, 即有多個索引指向了這個物件。List去元素時可以使用Iterator取出所有元素,在逐一遍歷,還可 以使用get(int index)獲取指定下表的元素;

(3)Map是雙列元素的集合,呼叫put(key,value),要儲存一對key/value,不能儲存重複的key, 這個是根據eauals來判斷;取元素時用get(key)來獲取key所對 應的value,另外還可以獲取 全部key,全部value

二. ArrayList 遍歷方式

另外本人整理了20年面試題大全,包含spring、並行、資料庫、Redis、分散式、dubbo、JVM、微服務等方面總結,下圖是部分截圖,需要的話點這裡點這裡,暗號CSDN。

在這裡插入圖片描述

  • 第 1 種,普通 for 迴圈隨機存取,通過索引值去遍歷。
// 隨機存取
     List<String> list = new ArrayList<>();
     int size = list.size();
     for (int i = 0; i < size; i++) {
         value = list.get(i);
     }
  • 第 2 種,通過迭代器遍歷。即通過 Iterator 去遍歷。
// 迭代器遍歷
    Iterator<String> iter = list.iterator();
    while (iter.hasNext()) {
        value = iter.next();
    }
  • 第 3 種,增強 for 迴圈遍歷。
 // 增強 for 迴圈
    for (String s : list) {
        value = s;
    }
  • 第 4 種 forEach + lambda 迴圈遍歷
list.forEach(p -> {
                p.hashCode();
            });

四種遍歷比較

  • 結論:如果資料量比較少的話貌似四種迴圈耗時都差不多,但是隨著資料量的增長會發現 foreach 的效率是最好的。但是從上面我們會發現一個奇怪的現象,第一次迴圈的時候forEach遍歷的時間是最長的儘管資料量非常少也會這樣。但是後面的耗時就正常了。如果放開測試裡面的預熱程式碼,每次跑出來的耗時也是正常的。

三、ArrayList和LinkedList的底層實現原理?他們為什麼執行緒不安全?在多執行緒並行操作下,我們應該用什麼替代?

1.ArrayList底層通過陣列實現,ArrayList允許按序號索引元素,而插入元素需要對陣列進行移位等記憶體操作,所以索引快插入較慢;(擴容方式)一旦我們範例化了ArrayList 無參建構函式預設陣列長度為10。add方法底層如 果增加的元素超過了10個,那麼ArrayList底層會生成一個新的陣列,長度為原來陣列長度的1.5倍+1,然後將原陣列內容複製到新陣列中,並且後續加的內容都會放到新陣列中。當新陣列無法容納增加元素時,重複該過程;

2.LinkedList底層通過雙向連結串列實現,取元素時需要進行前項或後項的遍歷,插入元素時只需要記錄本項的前後 項即可,所以插入快查詢慢;

3.ArrayList和LinkedList底層方法都沒有加synchronized關鍵詞,多執行緒存取時會出現多個執行緒先後更改資料造成得到的資料是髒資料;多執行緒並行操作下使用Vector來代替,Vector底層也是陣列,但底層方法都加synchronized關鍵字使執行緒安全,效率較ArrayList差;

四、HashMap和HashTable有什麼區別?其底層實現是什麼?CurrentHashMap的鎖機制又是如何?如果想將一個Map變為有序的,該如何實現?

1.區別:
(1)HashMap沒有實現synchronized執行緒非安全,HashTable實現了synchronized執行緒安全;
(2)HashMap允許key和value為null,而HashTable不允許

2.底層原理:陣列+連結串列實現

3.ConcurrentHashMap鎖分段技術:HashTable效率低下的原因,是因為所存取HashTable的執行緒都必須競爭同一把鎖,那假如容器中有多把鎖,每一把鎖用於鎖住容器中的一部分資料,那麼當多執行緒存取容器中不同的資料時,執行緒間就不會存在鎖競爭,從而提高並行存取率;ConcurrentHashMap使用的就是鎖分段技術,首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖存取其中一個資料時,其他段的資料也能被其他執行緒存取;

4.實現TreeMap

五.ArryList 注意點

謹慎使用 ArrayList 中的 subList 方法

  • ArrayList 的 subList 結果不可強轉成 ArrayList,否則會丟擲 ClassCastException 異常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 說明:subList 返回的是 ArrayList 的內部類 SubList,並不是 ArrayList ,而是 ArrayList 的一個檢視,對於 SubList 子列表的所有操作最終會反映到原列表上。
List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        ArrayList<String> strings =  (ArrayList)list.subList(0, 1);

執行結果:
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
  at com.workit.demo.listener.ArrayListTest.main(ArrayListTest.java:29)
  • 在 subList 場景中,高度注意對原集合元素個數的修改,會導致子列表的遍歷、增加、 刪除均會產 ConcurrentModificationException 異常。
 List<String> list = new ArrayList<>();
        list.add("1");
        list.add("1");
        list.add("2");
        List<String> subList =  list.subList(0, 1);
        // 對原 List 增加一個值
        list.add("10");
        subList.add("11"); // 這一行會報 java.util.ConcurrentModificationException
  • 初始化 List 的時候儘量指定它的容量大小。(儘量減少擴容次數)

最後:

針對最近很多人都在面試,我這邊也整理了相當多的面試專題資料,也有其他大廠的面經。希望可以幫助到大家。

下面的面試題答案都整理成檔案筆記。也還整理了一些面試資料&最新2020收集的一些大廠的面試真題(都整理成檔案,小部分截圖),有需要的可以點選進入暗號CSDN

在這裡插入圖片描述

在這裡插入圖片描述