-
Map
Map用於保存具有映射關系的數據,因此Map集合里保存着兩組值,一組值用於保存Map里的key,另一組值用於保存Map里的value,key和value都可以是任何引用類型的數據。Map的key不容許重復,即同一個Map對象的任何兩個key通過equals方法比較總是返回false。
key和value之間存在單向一對一關系,即通過指定的key,總能找到唯一的、確定的value。從Map中取出數據時,只要給出指定的key,就可以取出對應的value。
如果把Map里的所有key放在一起看,它們就是一個Set集合,實際上Map確實包含一個keySet()方法,用於返回Map所有key組成的Set集合。如下:
public class Test { public static void main(String[] args){ Map<String, String> mapVal = new HashMap<String, String>(); mapVal.put("spring", "春"); mapVal.put("summer", "夏"); mapVal.put("autumn", "秋"); mapVal.put("winter", "冬"); //獲取mapVal集合的所有key值 Set<String> set = mapVal.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String season = it.next(); System.out.println(season); } } }
不僅如此,Map里key集合和Set集合里元素的存儲形式也很像,Map子類和Set子類在名字上也驚人的相似:如Set接口下有HashSet、LinkedHashSet、SortedSet(接口)、TreeSet、EnumSet等實現類和子接口,而Map接口下則有HashMap、LinkedHashMap、SortedMap(接口)、TreeMap、EnumMap等實現類和子接口。正如它們名字所暗示的,Map的這些實現類和子接口中key集存儲形式和對應Set集合中元素的存儲形式完全相同。
如果把Map所有value放在一起看,它們又非常類似於一個List:元素與元素之間可以重復,每個元素可以根據索引來查找,只是map中的索引不再使用整數值,而是以另一個對象做為索引。如果需要從List集合中取元素,需要提供該元素的數字索引;如果需要從Map中取出元素,需要提供該元素的key索引。因此,Map有時也被稱為字典,或關聯數組。Map接口中定義了如下常用方法:
- void clear(); //刪除該Map對象中所有key-value對。
- boolean containsKey(Object key); //查詢Map中是否包含指定key,如果包含則返回true。
- boolean containsValue(Object value); //查詢Map中是否包含一個或多個value,如果包含則返回true。
- Set entrySet(); //返回Map中所包含的key-value對所組成的Set集合,每個集合元素都是Map.Entry(Entry是Map的內部類)對象。
- Object get(Object key); //返回指定key所對應的value,如果此Map不包含該key,則返回null。
- boolean isEmpty(); //查詢該Map集合是否為空(即不包含任何key-value對),如果為空則返回true。
- Set keySet(); //返回該Map中所有key所組成的Set集合。
- Object put(Object key, Object value); //添加一個key-value對,如果當前Map中已有一個與key相等的key-value對,則新的key-value對會覆蓋原來的key-value對。
- void putAll(Map m); //將指定Map中的key-value對復制到本Map中。
- Object remove(Object key); //刪除指定key所對應的key-value對,返回被刪除key所關聯的value,如果key不存在,返回nul
- int size(); //返回該Map里的key-value對的個數。
- Collection values(); //返回該Map里所有value組成的Collection。
Map中包含一個內部類:Entry。該類封裝了一個key-value對,Entry包含三個方法:
-
- Object getKey(); //返回該Entry里包含的key值。
- Object getValue(); //返回該Entry里包含的value值。
- Object setValue(V value); //設置該Entry里包含的value值,並返回新設置的value值。
我們可以把Map理解成一個特殊的Set,只是該Set里包含的集合元素是Entry對象,而不是普通對象。
如下為Entry示例:
public class Test { public static void main(String[] args){ Map<String, String> mapVal = new HashMap<String, String>(); mapVal.put("spring", "春"); mapVal.put("summer", "夏"); mapVal.put("autumn", "秋"); mapVal.put("winter", "冬"); Set<Entry<String, String>> entrySet = mapVal.entrySet(); Iterator<Entry<String, String>> iterator = entrySet.iterator(); while(iterator.hasNext()){ Entry<String, String> next = iterator.next(); String key = next.getKey(); String value = next.getValue(); System.out.println("key:"+ key +"-------value:" + value); } } }
-
HashMap和Hashtable實現類
HashMap和Hashtable都是Map接口的典型實現類,他們之間的關系完全類似於ArrayList和Vector的關系:Hashtable是一個古老的Map實現類,它從JDK1.0起就已經出現了,當它出現時,Java沒有提供Map接口,所以它包含了兩個繁瑣的方法:elements()(類似於Map接口定義的values()方法)和keys(類似於Map接口定義的keySet()方法),現在很少使用這兩個方法。
HashMap和Hashtable的兩點典型區別:
Hashtable是一個線程安全的Map實現,但HashMap是線程不安全的實現,所以HashMap比Hashtable性能要高一點;但如果有多條線程訪問同一個Map對象時,使用Hashtable實現類會更好。
Hashtable不容許使用null作為key和value,如果試圖把null放進Hashtable中,將會引發NullPointerException異常;但HashMap可以使用null做為key和value。
注意:與Vector類似,盡量少用Hashtable實現類,即使需要創建線程安全的Map實現類,也可以通過Collections工具類把HashMap變成線程安全的,無須使用Hashtable實現類。
為了成功地在HashMap、Hashtable中存儲、獲取對象,用作key的對象必須實現hashCode方法和equals方法。
與HashSet不能保證元素的順序一樣,HashMap和Hashtable也不能保證key-value對的順序。類似於HashSet的是,HashMap、Hashtable判斷兩個key相等的標准也是:兩個key通過equals方法比較返回true,兩個key的 hashCode值也相等。
除此之外,HashMap、Hashtable中還包含一個containsValue方法用於判斷是否包含指定的value,那么HashMap、Hashtable如何判斷兩個value相等呢?HashMap、Hashtable判斷兩個value相等的標准更簡單:只要兩個對象通過equals比較返回true即可。
-
LinkedHashMap類
HashMap有一個子類:LinkedHashMap;LinkedHashMap也使用雙向鏈表來維護key-value對的次序,該鏈表定義了迭代順序,該迭代順序與key-value對的插入順序保持一致。LinkedHashMap可以避免需要對HashMap、Hashtable里的key-value對進行排序(只要插入key-value對時保持順序即可)。同時又避免使用TreeMap所增加的成本。
LinkedHashMap需要維護元素的插入順序,因此性能略低於HashMap的性能,但在迭代訪問Map里的全部元素時將有很好的性能,因為它以鏈表來維護內部順序。
public class Test { public static void main(String[] args){ LinkedHashMap<String, String> mapVal = new LinkedHashMap<String, String>(); mapVal.put("spring", "春"); mapVal.put("summer", "夏"); mapVal.put("autumn", "秋"); mapVal.put("winter", "冬"); System.out.println(mapVal); for(String str : mapVal.keySet()){ System.out.println("key:" + str); System.out.println("value:" + mapVal.get(str)); } } }
-
Properties類
Properties類是Hashtable類的子類,正如它的名字所暗示的,該文件在處理屬性文件。 Properties類可以把Map對象和屬性文件關聯起來,從而可以把Map對象中的key-value對寫入屬性文件,也可以把屬性文件中的屬性名=屬性值加載到Map對象中。由於屬性文件里的屬性名、屬性值只能是字符串類型,所以Properties里的key、value都是字符串類型,該類提供了如下三個方法來修改Properties里的key、value值。
-
- String getProperty(String key); //獲取properties中指定屬性名對應的屬性值,類似於Map的get(Object key)方法。
- String getProperty(String key, String defaultValue); //該方法與前一個方法基本相似,該方法多個功能,如果Properties中不存在指定key時,該方法返回默認值。
- Object setProperty(String key, String value); //設置屬性值,類似Hashtable的put方法。
- void load(InputStream inStream); //從屬性文件(以輸出流表示)中加載屬性名=屬性值,把加載到的屬性名=屬性值對追加到Properties里(由於Properties是Hashtable的之類,它不保證key-value對之間的次序)。
- void store(OutputStream out, String comments); //將Properties中的key-value對寫入指定屬性文件(以輸出流表示),comments是要寫的注解。
如下代碼所示:
public class Test { public static void main(String[] args){ Properties readProperties = new Properties(); //用來寫的Properties對象 readProperties.setProperty("username", "zhangsan"); readProperties.setProperty("password", "123456"); Properties writeProperties = new Properties(); //用來讀的Properties對象 FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { //1.1通過類裝載器獲取要存儲的路徑 String path = Test.class.getResource("/").getPath(); path = path + "config.properties"; System.out.println(path); fileOutputStream = new FileOutputStream(new File(path)); //1.2將配置文件信息寫到硬盤上 readProperties.store(fileOutputStream, "This is config with database!"); //2.1讀取硬盤上的配置文件 fileInputStream = new FileInputStream(new File(path)); writeProperties.load(fileInputStream); String username = writeProperties.getProperty("username"); String username2 = writeProperties.getProperty("username2" , "沒有找到username2的key"); //打印結果"sername:zhangsan" System.out.println("username:" + username); //打印結果"username2:沒有找到username2的key" System.out.println("username2:" + username2); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(fileOutputStream != null){ try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
-
SortedMap接口和TreeMap實現類
正如Set接口派生出了SortedSet子接口,SortedSet接口有一個TreeSet實現類,Map接口也派生了一個SortedMap子接口,SortedMap也有一個TreeMap實現類。
與TreeSet類似的是,TreeMap也是基於紅黑樹對TreeMap中所有key進行排序,從而保證TreeMap中所有key-value對處於有序狀態。TreeMap也有兩種排序方式:
自然排序:TreeMap的所有key必須實習那Comparable接口,而且所有key應該是同一個類的對象,否則將會拋出ClassCaseException。
定制排序:創建TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中所有key進行排序。采用定制排序時不要求Map的key實現Comparable接口。
*可以參考TreeSet的代碼演示。
-
WeakHashMap實現類
WeakHashMap與HashMap的用法基本相似。但與HashMap的區別在於,HashMap的key保留對象的強引用,這意味着只要該HashMap對象不被銷毀,該HashMap對象所有key所引用的對象不會被垃圾回收,HashMap也不會自動刪除這些key所對應的key-value對象;但WeakHashMap的key只保留對實際對象的弱引用,這意味着當垃圾回收了該key所對應的實際對象后,WeakHashMap會自動刪除該key對應的key-value對。
public class Test { public static void main(String[] args){ WeakHashMap<String, String> map = new WeakHashMap<String, String>(); //將WeakHashMap中添加三個key-value對, //三個key都是匿名字符串對象(沒有其他引用) map.put(new String("語文"), new String("優")); map.put(new String("數學"), new String("良")); map.put(new String("英語"), new String("中")); //將WeakHashMap中添加一個key-value對, //該key是一個系統緩存的字符串對象。 map.put("java", new String("不及格")); //輸出map對象,將看到4個key-value對{java=不及格, 數學=良, 英語=中, 語文=優} System.out.println(map); //通知系統立即進行垃圾回收 System.gc(); System.runFinalization(); //通常情況下,將只看到一個key-value對{java=不及格} System.out.println(map); } }
從上面運行結果可以看出,當系統進行垃圾回收時,刪除了WeakHashMap對象的前三個key-value對。這是因為添加前三個key-value對時,這三個key都是匿名字符串對象,只有WeakHashMap保留了對它們的弱引用。WeakHashMap對象中的第四組key-value對的key是一個字符串的直接量,系統會緩沖這個字符串直接量(即系統保留了對該字符串對象的強引用),所以垃圾回收時不會回收它。
-
IdentityHashMap實現類
IdentityHashMap實現類的實現機制與HashMap基本相似,但它在處理兩個key相等時,比較獨特:在IdentityHashMap中,當且僅當兩個key嚴格相等時(key1 = key2)時,IdentityHashMap才認為兩個key相等,對於普通HashMap而言,只要key1和key2通過equals比較返回true,且它們的hashCode值相等即可。
IdentityHashMap提供了與HashMap基本相似的方法,也允許使用null做為key和value。與HashMap類似的是,IdentityHashMap不保證任何key-value對之間的順序,更不能保證它們的順序隨時間的推移保持不變。
public class Test { public static void main(String[] args){ IdentityHashMap<String, String> map = new IdentityHashMap<String, String>(); //下面兩行代碼會向map中添加兩條key-value對 map.put(new String("語文"), "99"); map.put(new String("語文"), "100"); //下面兩行代碼會向map中添加一條key-value對 map.put("java", "89"); map.put("java", "69"); //打印結果為{java=69, 語文=100, 語文=99} System.out.println(map); } }
-
EnumMap實現類
EnumMap是一個與枚舉類一起使用的Map實現,EnumMap中所有key都必須是單個枚舉類的枚舉值。創建EnumMap時必須顯示或隱式指定它對應的枚舉類。
EnumMap不允許使用null作為key值,但容許使用null值做為value。如果試圖使用null做為key將拋出NullPointerException異常。如果僅僅只是查詢是否包含值為null的key,或者僅僅只是使用刪除值為null的key,都不會拋出異常。
enum Season{ SPRING,SUMMER,AUTUMN,WINTER } public class Test { public static void main(String[] args){ EnumMap<Season, String> map = new EnumMap<Season, String>(Season.class); //打印結果為{} System.out.println(map); map.put(Season.SPRING, "春"); map.put(Season.SUMMER, "夏"); map.put(Season.AUTUMN, "秋"); map.put(Season.WINTER, "冬"); //打印結果為{SPRING=春, SUMMER=夏, AUTUMN=秋, WINTER=冬} System.out.println(map); } }
上面程序中創建了一個EnumMap對象,創建該EnumMap對象時指定它的key只能是Season枚舉類的枚舉值。如果向該EnumMap中添加四個key-value對后,這四個key-value對將會以Season枚舉值的自然順序排序。
對於Map的常用實現類而言,HashMap和Hashtable的效率大致相同,因為它們的實現機制幾乎完全一樣,但HashMap通常比Hashtable要快一點,因為Hashtable額外實現同步操作。
TreeMap通常比HashMap、Hashtable要慢(尤其在插入、刪除key-value對的時候更慢),因為TreeMap需要額外的紅黑樹操作來維護key之間的次序。但使用TreeMap有一個好處:TreeMap中的key-value對總是處於有序狀態,無須專門進行排序操作。