一般來說,Map是一種由鍵值對組成的數據結構,其中鍵(key)在Map中是不能重復的;
本篇文章總結了9個最常見的問題(關於Java Map及其實現類);
出於簡單考慮,在代碼例子中我將不使用泛型,因此,我將僅僅寫上Map而不是寫上Map<K, V>,且Map中的Key和Value都是可以比較的,意味著K和V都實現了Comparable接口;
在Java中,Map接口提供了三個集合視圖,即key set, value set,和key-value set。它們都可以轉成List集合,如下代碼所示:
// key list List keyList = new ArrayList(map.keySet()); // value list List valueList = new ArrayList(map.valueSet()); // key-value list List entryList = new ArrayList(map.entrySet());
迭代遍歷Map中的鍵值對是最基本的操作。在Java中,鍵值對信息存儲在Map.Entry類中。通過調用Map.entrySet()方法會返回鍵值對集合,因此最有效的遍歷Map中的鍵值對如下代碼所示:
for(Entry entry: map.entrySet()) { // get key K key = entry.getKey(); // get value V value = entry.getValue(); }
當然,也可以通過迭代器遍歷,如下代碼:
while(itr.hasNext()) { Entry entry = itr.next(); // get key K key = entry.getKey(); // get value V value = entry.getValue(); }
對Map中的元素按key排序是另一個經常使用的操作。有一種方法是將Map中的元素放進List集合,然後通過一個比較器對集合進行排序,如下代碼示例:
List list = new ArrayList(map.entrySet()); Collections.sort(list, new Comparator() { public int compare(Entry e1, Entry e2) { return e1.getKey().compareTo(e2.getKey()); } }); }
另一種方法是使用SortedMap,其提供了對Key進行排序的功能;前提是Map中的Key需要實現Comparable接口或者通過構造方法傳入比較器;
TreeMap是SortedMap的一個實現類,它的構造方法能夠接收一個比較器,以下代碼展示了如何將普通Map轉成SortedMap:
SortedMap sortedMap = new TreeMap(new Comparator() { @Override public int compare(K k1, K k2) { return k1.compareTo(k2); } }); sortedMap.putAll(map);
同樣可以參考前面的將Map的元素放進List集合然後傳入比較器進行排序,不過這次要比較的對象是Entry.getValue()。如下代碼所示:
List list = new ArrayList(map.entrySet()); Collections.sort(list, new Comparator() { @Override public int compare(Entry e1, Entry e2) { return e1.getValue().compareTo(e2.getValue()); } });
對於這個問題,當map中的value都是唯一的情況,我們仍然可以使用sorted map來排序,在這種情況下,你可以將key賦值給value,value賦值給key來實現該功能;但是這種解決方法十分受限,非常不建議使用;
當你想要使用Map來存儲一些常量時,將他們存儲到一個不可修改的Map是一個好習慣。這種防御性編程技術會幫助你創建一個能安全使用且線程安全的map;
我們可以使用靜態代碼塊來初始化一個靜態不可變的map,如下代碼:
public class Test { private static final Map map; static { map = new HashMap(); map.put(1, "one"); map.put(2, "two"); } }
然而以上代碼是有問題的,即使map被聲明成static final,我們仍然可以操作該map,如Test.map.put(3,"three");因此,這不是不可變的map;
為了創建一個不可變的map,我們需要一個額外的map對象,將它作為入參傳入Collections.unmodifiableMap方法中,然後得到一個不可變的Map,如下代碼:
public class Test { private static final Map map; static { Map aMap = new HashMap(); aMap.put(1, "one"); aMap.put(2, "two"); map = Collections.unmodifiableMap(aMap); } }
此時,當我們運行Test.map.put(3,"three")將拋出UnsupportedOperationException異常;
Google的Guava庫支持多種方式創建不可變的Map,想學習的可以參考其官方使用手冊;
HashMap, TreeMap, 和Hashtable是Map接口的三個主要實現類,它們之間的主要區別如下:
另一個比較完整的比較如下:
它們之間的更多比較信息可參考如下鏈接:
http://www.programcreek.com/2013/03/hashmap-vs-treemap-vs-hashtable-vs-linkedhashmap/
有時候,我們需要key和value都唯一,這種情況允許我們創建一個雙向查找Map,也就是說,可以通過唯一的value去查找唯一的Key,遺憾的是,JDK並不支持該數據結構;
Apache Common Collections 和 Guava 提供了對雙向查找Map的支持,被叫做BidiMap和BiMap,它們規定了key和value必須是1對1的關系。
大部分Map實現類都提供了構造方法,用於對另外一個Map的復制,但是這個復制操作不是同步的,意味著一個線程在對Map進行復制操作的時候,另一個線程可能會對它結構性的修改(增刪該等操作);為了阻止意外的非同步操作,應該事先使用Collections.synchronizedMap()方法去創建一個同步Map,如下:
Map copiedMap = Collections.synchronizedMap(map);
另外一種淺拷貝方法是使用clone()方法,然而它並不被集合框架作者Josh Bloch推薦;他曾在一次訪談中談到關於”拷貝構造方法VS克隆方法“,他說:
I often provide a public clone method on concrete classes because people expect it. ... It’s a shame that Cloneable is broken, but it happens. ... Cloneable is a weak spot, and I think people should be aware of its limitations.
如果是不可變Map,使用Collections工具類創建,如下:
map = Collections.emptyMap();
否則,直接new,如下:
map = new HashMap();
譯文鏈接:http://www.programcreek.com/2013/09/top-9-questions-for-java-map/