【第18天】Java集合(五)---Map介面概述及Map介面實現的HashMap類、SortedMap介面實現的TreeMap類
1 Map的通性
一種鍵值對型別的集合,每次向集合新增一對元素,新增“主鍵Key=值Value”。其中只能在新建時傳入且唯一,傳入後不得修改;值不唯一,可直接修改。
1.1 基本用法與特點
-
建立Map物件
HashMap<主鍵型別,值型別> map = new HashMap<>(); TreeMap<主鍵型別,值型別> map = new TreeMap<>();
-
新增元素
map.put(主鍵,值); map1.putAll(map2);
-
得到集合的大小
map.size();
-
根據主鍵得到值物件
map.get(主鍵); -> 值
-
判斷集合裡是否出現指定的主鍵物件
map.containsKey(主鍵);
-
判斷集合裡是否出現指定的值物件
map.containsValue(值);
-
根據主鍵刪除元素
map.remove(主鍵);
-
若想快速判斷Map中某個鍵/值是否存在,要善用contains()方法。根據類中定義好的equals()方法,檢視是否存在,可以直接返回boolean值。同樣使用remove(主鍵)時,也可以通過equals()方法定製刪除主鍵的規則。
1.2 遍歷
姓名 | 得分 |
---|---|
- 通過Map集合得到所有的主鍵物件(主鍵物件是唯一且無序的,故使用Set來接收即可)能獲取到的是:
姓名 |
---|
Set<主鍵> set = map.keySet();
得到主鍵的同時,主鍵是唯一的,可以根據主鍵得到值物件:map.get(主鍵),只能根據主鍵得到對應的值物件而不能改。
- 通過Map集合得到所有的值物件 (主鍵物件是不唯一且無序的,故只能使用Collection來接收),能獲取到的是:
得分 |
---|
Collection<值> coll = map.values();
無法根據值得到對應的主鍵,因為值是不唯一的,只能將值取出。
-
通過Map集合得到所有記錄的主鍵和值且可以對值進行修改
Set<Map.Entry<主鍵,值>> set = map.entrySet(); 對set遍歷可以獲取到每一條記錄的主鍵和值 通過記錄得到主鍵物件:記錄.getKey() 通過記錄得到值物件:記錄.getValue() 通過記錄修改值物件:記錄.setValue(XXX)
能獲取到的是一條一條的:
姓名 | 得分 |
---|---|
//將Eric的成績 + 20分
import java.util.*;
public class Test{
public static void main(String[] args){
HashMap<String,Integer> map = new HashMap<>();
//新增元素:
map.put("Allen",455);
map.put("Eric",122);
map.put("Cindy",600);
//通過Map集合得到一條記錄 -> [主鍵,值]
Set<Map.Entry<String,Integer>> set = map.entrySet();
//通過對集合每一條記錄遍歷,得到x -> 即集合裡面的每一條記錄
for(Map.Entry<String,Integer> x : set){
//通過記錄得到主鍵物件
String name = x.getKey();
//通過記錄得到值物件
Integer score = x.getValue();
if(name.equals("Eric")){
x.setValue(score + 20);
}
}
System.out.println(set);//--->[Cindy=122, Allen=455, Eric=620]
System.out.println(map);//--->[Cindy=122, Allen=455, Eric=620]
}
}
-
因Map是一個鍵值對,當單獨取值key或value時,不要對其進行修改,只作為一個遍歷查詢的簡便方法。對單獨取值得到的集合進行add()操作會報UnsupportOperationException。但可以對Set型別的主鍵集合中元素進行刪除,進而Map中整個元素被刪除,因為其具有唯一性。
-
主鍵不能直接修改,若修改需要先remove(主鍵)之前的,再put(主鍵,值)進新的鍵值對。
-
值可以修改,使用setValue() 或 直接使用put(舊主鍵,新值)進行覆蓋。
-
在使用迭代器或foreach進行遍歷時注意CME異常,要使用迭代器的remove方法,增加新元素時需要使用新集合接收,在迴圈外將新集合加入舊集合。
2 HashMap集合的特性
- Map介面的實現類,方法與Map的通用方法相同,但底層需遵循之前學過的hashCode()、==、equals()的比較機制。
- HashMap的特性與HashSet的其他特性也類似,比如在對集合中元素修改時,不能對已傳入的元素的與雜湊特徵值生成有關的屬性進行直接修改。
import java.util.*;
public class Test{
public static void main(String[] args){
HashMap<Student,Integer> map = new HashMap<>();
Student s1 = new Student("張三", 18);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 26);
Student s4 = new Student("zz", 25);
map.put(s1,77);
map.put(s2,88);
System.out.println(map.containsKey(s4));//--->true
//因Student物件的hashCode()和equals()都一樣
//張三、李四被視為相等物件,新加入的李四物件不會加入集合,但年齡所對應的Integer類hashCode()值有不同,故可以加入集合
//同理可以考慮主鍵名不變,修改value值可以put(舊主鍵名, 新值)直接覆蓋
//傳入的主鍵相同,但主鍵是唯一的,所以不再新增新的,但value值不同,故可以新增
System.out.println(map);//--->{張三=88}
map.remove(s3);
System.out.println(map);//--->{}
}
}
class Student{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return name;
}
@Override
public boolean equals(Object obj){
return true;//任何兩個物件都視為相等物件
}
@Override
public int hashCode(){
return 1;//任何物件的雜湊碼值都是1
}
}
-
HashMap和Hashtable之間的區別(Hasntable為最早的Map集合形式)
-
同步特性
Hashtable同一時時間允許一個執行緒進行訪問,效率較低,但是不會出現併發錯誤
HashMap同一時間允許多個執行緒進行訪問,效率較高,但是可能會出現併發錯誤JDK5.0開始集合的工具類(Collections)裡面提供一個方法(synchroniedMap()) 可以將執行緒不安全的HashMap變成執行緒安全的集合物件,所以Hashtable被淘汰。但添加了synchronizedMap()之後的HashMap與HashTable無異,都是在分組前上鎖來控制傳入的資料,這時只能逐個進入分組;
JDK5.0後不如使用util包下concurrent子包中的ConcurrentHashMap集合,對每個小組加鎖,允許進入多個數據,效率更高。 -
對null容忍度不同
HashMap無論是主鍵還是值物件,都可以存放null。由於主鍵是唯一,所以主鍵只能存放一個null,Hashtable無論是主鍵還是值物件,都不能存放null,否則都會空指標異常。 -
底層分組不同
HashMap預設分16個小組,程式設計師可以按照自己的意願隨意的進行指定分組組數,但是最終一定是2的n次方數,比如定義為17組,最後系統會自動開闢32組。
Hashtable預設分11個小組,程式設計師可以按照自己的意願隨意指定分組組數。 -
出現的版本不同
Hashtable適用於JDK1.0及以後,
HashMap適用於JDK1.2及以後。
-
3 TreeMap集合的特性
- SortedMap介面的實現類,方法與Map的通用方法相同,但底層需遵循之前學過的compareTo()/compare的比較機制。
- TreeMap的特性與TreeSet的其他特性也類似,比如在對集合中元素修改時,不能對已傳入的元素的與compareTo()/compare有關的屬性進行直接修改。
import java.util.*;
public class Test{
public static void main(String[] args){
TreeMap<Student,Integer> map = new TreeMap<>();
Student s1 = new Student("張三",18);
map.put(s1,77);
//compareTo()一直向右比較,因返回值無法返回0而不會停止比較,所以remove()方法無法匹配並返回值
map.remove(s1);
System.out.println(map);//--->{張三=77}
//compareTo()一直向右比較,因返回值無法返回0而不會停止比較,所以containsKey()方法無法匹配並返回值
System.out.println(map.containsKey(s1));//--->false
//compareTo()一直向右比較,因返回值無法返回0而不會停止比較,所以get()方法無法匹配並返回值
System.out.println(map.get(s1));//--->null
System.out.println(map.size());//--->1
map.put(s1,77);
//因compareTo()返回值為1可以無限向右子樹新增相同的元素
System.out.println(map.size());//--->2
}
}
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return name;
}
@Override
public int compareTo(Student stu){
return 1;
}
}