(1) TreeMap有哪些特徵
答:TreeMap底層實現使用紅黑樹實現,TreeMap中存儲的鍵值對按照鍵來排序。
-
如果Key存入的是字符串等類型,那麼會按照字典序默認排序
-
如果傳入的是自定義引用類型,比如說User,那麼該對象必須實現Comparable接口,並且覆蓋其compareTo,或者在創建TreeMap的時候,我們必須指定使用的比較器,
// 方式一:定義該類的時候,就指定比較規則 class User implements Comparable{ @Override public int compareTo(Object o) { // 在這裏邊定義其比較規則 return 0; } } public static void main(String[] args) { // 方式二:創建TreeMap的時候,可以指定比較規則 new TreeMap<User, Integer>(new Comparator<User>() { @Override public int compare(User o1, User o2) { // 在這裏邊定義其比較規則 return 0; } }); }
關於TreeMap的考察,會涉及到兩個接口Comparable和Comparator的比較。Comparable接口的後綴able大概表示可以的意思,也就是說一個類如果實現了這個接口,那麼這個類就是可以比較的。類似還有cloneable接口表示可以克隆的。而Comparator則是一個比較器,是創建TreeMap的時候傳入,用來指定比較規則。
Comparaable接口和Comparator接口的區別
-
Comparable實現比較簡單,但是當需要重新定義比較規則的時候,必須修改代碼,即修改User類裏邊的compareTo方法
-
Comparator接口不需要修改源代碼,只需要在創建TreeMap的時候重新傳入一個具有指定規則的比較器即可。
(2)ArrayList和LinkedList的區別
常用的ArrayList和LinkedList的區別如下:
-
ArrayList底層實現使用了動態數組實現,實質上是一個動態數組
-
LinkedList底層實現使用了雙向鏈表實現,可當作堆棧,隊列,雙端隊列使用
-
ArrayList在隨機存取方面效率高於LinkedList
-
LinkedList在節點的增刪方面效率高於ArrayList
-
ArrayList必須預留一定的空間,當空間不足的時候,會進行擴容操作
-
LinkedList的開銷是必須存儲節點的信息以及節點的指針信息
解析:
List集合也是我們平時使用很多的集合。List接口的長劍實現就算ArrayList和LinkedList,我們必須熟練掌握其底層實現以及一些特徵。其實好友一個集合Vector,它是線程安全的ArrayList,但是已經被廢棄,不推薦使用了。多線程環境下,我們可以使用CopyOnWriteArrayList替代ArrayList來保證線程安全。
(3)HashSet和TreeSet的區別
HashSet和TreeSet的區別:
-
HashSet底層實現使用了Hash表實現。(保證元素唯一性原理:判斷元素的hashCode值是否相同。如果相同,還會繼續判斷元素的equals方法,是否爲true)
-
TreeSet底層使用了紅黑樹來實現。(保證元素唯一性是通過Comparable和Comparator接口實現)
解析:
其實,HashSet的底層實現還是HashMap,只不過其使用了其中的Key,具體如下所示:
-
HashSet的add方法底層使用HashMap的put方法將key = e,value=PRESENT構建成key-value鍵值對,當此e存在於HashMap的key中,則value將會覆蓋原有value,但是key保持不變,所以如果將一個已經存在的e元素添加到HashSet中,新添加的元素是不會保存到HashMap中,所以就滿足了HashSet中元素不會重複的特性。
-
HashSet的contains方法使用HashMap的containsKey方法實現
(4)LinkedHashMap和LinkedHashSet有了解嗎?
LinkedHashMap可以記錄下元素的插入順序和訪問順序,具體實現如下:
-
LinkedHashMap內部的Entry繼承於HashMap.Node,這兩個類都實現了Map.Entry<K,V>
-
LinkedHashMap的Entry不光有value,next,還有before和after屬性,這樣通過一個雙向鏈表,保證了各個元素的插入順序
-
通過構造方法public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder), accessOrder傳入true可以實現LRU緩存算法(訪問順序)
-
LinkedHashSet 底層使用LinkedHashMap實現,兩者的關係類似與HashMap和HashSet的關係,大家可以自行類比。
擴展: 什麼是LRU算法?LinkedHashMap如何實現LRU算法?
LRU(Least recently used,最近最少使用)算法根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是“如果數據最近被訪問過,那麼將來被訪問的機率也更高”。
由於LinkedHashMap可以記錄下Map中元素的訪問順序,所以可以輕易的實現LRU算法。只需要將構造方法的accessOrder傳入true,並且重寫removeEldestEntry方法即可。具體實現參考
package pak2; import java.util.LinkedHashMap; import java.util.Map; public class LRUTest { private static int size = 5; public static void main(String[] args) { Map<String, String> map = new LinkedHashMap<String, String>(size, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { return size() > size; } }; map.put("1", "1"); map.put("2", "2"); map.put("3", "3"); map.put("4", "4"); map.put("5", "5"); System.out.println(map.toString()); map.put("6", "6"); System.out.println(map.toString()); map.get("3"); System.out.println(map.toString()); map.put("7", "7"); System.out.println(map.toString()); map.get("5"); System.out.println(map.toString()); } }
-
List是有序的並且元素是可以重複的
-
Set是無序(LinkedHashSet除外)的,並且元素是不可以重複的(此處的有序和無序是指放入順序和取出順序是否保持一致)
(6)Iterator和ListIterator的區別是什麼?
-
Iterator可以遍歷list和set集合;ListIterator只能用來遍歷list集合
-
Iterator前者只能前向遍歷集合;ListIterator可以前向和後向遍歷集合
-
ListIterator其實就是實現了前者,並且增加了一些新的功能。
解析:
Iterator其實就是一個迭代器,在遍歷集合的時候需要使用。Demo實現如下:
ArrayList<String> list = new ArrayList<>(); list.add("zhangsan"); list.add("lisi"); list.add("yangwenqiang"); // 創建迭代器實現遍歷集合 Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
package niuke; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ConverTest { public static void main(String[] args) { // list集合轉換成數組 ArrayList<String> list = new ArrayList<>(); list.add("zhangsan"); list.add("lisi"); list.add("yangwenqiang"); Object[] arr = list.toArray(); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("---------------"); // 數組轉換爲list集合 String[] arr2 = {"niuke", "alibaba"}; List<String> asList = Arrays.asList(arr2); for (int i = 0; i < asList.size(); i++) { System.out.println(asList.get(i)); } }
關於數組和集合之間的轉換是一個常用操作,這裏主要講解幾個需要注意的地方吧。
數組轉爲集合List:
通過Arrays.asList方法搞定,轉換之後不可以使用add/remove等修改集合的相關方法,因爲該方法返回的其實是一個Arrays的內部私有的一個類ArrayList,該類繼承於Abstractlist,並沒有實現這些操作方法,調用將會直接拋出UnsupportOperationException異常。這種轉換體現的是一種適配器模式,只是轉換接口,本質上還是一個數組。
集合轉換數組:
List.toArray方法搞定了集合轉換成數組,這裏最好傳入一個類型一樣的數組,大小就是list.size()。因爲如果入參分配的數組空間不夠大時,toArray方法內部將重新分配內存空間,並返回新數組地址;如果數組元素個數大於實際所需,下標爲list.size()及其之後的數組元素將被置爲null,其它數組元素保持原值。所以,建議該方法入參數組的大小與集合元素個數保持一致。
若是直接使用toArray無參方法,此方法返回值只能是Object[ ]類,若強轉其它類型數組將出現ClassCastException錯誤。
(8)Collection和Collections有什麼關係?
這是Java中的一類問題,類似的還有Array和Arrays,Executor和Executors有什麼區別與聯繫? (待補)
附圖:
集合的類圖:
我們接着給出本節所涉及到的集合的類圖結構:(C表示這是一個類,I表示這是一個接口)
-
-
-
Vector的類圖結構:
-