一、映射表(Map)數據結構
集是一個集合,它可以快速的查找現有的元素。但是,要查看一個元素,需要有查找元素的精確副本。這不是一種非常通用的查找方式。通常,我們知道某些鍵的信息,並想要查找與之對應的元素。映射表(map)數據結構就是爲此設計的。映射表用來存放鍵/值對。如果提供了鍵,就能找到值。Java類庫爲映射表提供了兩個通用的實現:HashMap和TreeMap。這兩個類都實現了Map接口。
二、Map<K,V>接口
1、Map<K,V>簡介
a)、Map接口是用於保存具有映射關係的數據,Map存儲的是鍵值對的形式的元素,它的每一個元素,都是由鍵和值兩個元素組成,它的鍵要求唯一,值可以重複,將鍵映射到值的對象,一個映射不能包含重複的鍵,每個鍵最多隻能映射到一個值。
b)、迭代Map接口中的元素不存在直截了當的方法,如果要查詢某個Map以瞭解其哪些元素滿足特定的查詢,或要迭代其它所有元素。則我們首先需要獲取該Map的視圖。Map接口提供了三種Collection視圖:
- 所有鍵值對(鍵值對)entrySet()
- 所有鍵(鍵集)keySet()
- 值(值集),values()
三種視圖如下表所示:
entrySet() | 返回 Map 中所包含映射的 Set 視圖。Set 中的每個元素都是一個 Map.Entry 對象,可以使用 getKey() 和 getValue() 方法(還有一個 setValue() 方法)訪問後者的鍵元素和值元素 |
keySet() | 返回 Map 中所包含鍵的 Set 視圖。刪除 Set 中的元素還將刪除 Map 中相應的映射(鍵和值) |
values() | 返回 map 中所包含值的 Collection 視圖。刪除 Collection 中的元素還將刪除 Map 中相應的映射(鍵和值) |
c)、Map接口中常用的實現類
- HashMap:底層是哈希表數據結構,允許使用null值和null鍵,實現不同步,效率較高。不保證映射順序。
- Hashtable:底層是哈希表數據結構,不允許存入null值和null鍵,實現同步,效率低,已被HashMap替代。
- TreeMap:基於紅黑樹實現,底層是樹結構,線程不同步,可以用於給Map集合中的鍵進行排序,排序原理與TreeSet相似。Set底層就是用Map實現的。
2、Map接口的共性方法
a)、添加方法:
b)、刪除方法:
c)、判斷方法:
- containsKey(Object key):如果此映射包含指定鍵的映射關係,則返回 true
- containsValue(Object value):如果此映射將一個或多個鍵映射到指定值,則返回 true
- equals(Object o):比較指定的對象與此映射是否相等
- isEmpty():如果此映射未包含鍵-值映射關係,則返回 true
d)、獲取方法:
- get(Object key):返回指定鍵所映射的值;如果此映射不包含該鍵的映射關係,則返回 null
- hashCode():返回此映射的哈希碼值
- size():返回此映射中的鍵-值映射關係數
- Collection<V> values():返回此映射中包含的值的Collection 視圖
- Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射關係的Set 視圖。
- Set<K> keySet():返回此映射中包含的鍵的 Set 視圖。
Map集合常用方法示例:
import java.util.*;
/*
Map集合:該集合存儲鍵值對,一對一對往裏存,而且要保證元素的唯一性。
常用方法演示。
*/
public class MapTest1{
public static void main(String[] args){
//創建一個Map集合
Map<String,String> map = new HashMap<String,String>();
//添加元素
map.put("01","zhangsan1");
map.put("02","zhangsan2");
map.put("03","zhangsan3");
/*
添加元素,如果出現添加相同的鍵。那麼後添加的值會覆蓋原有鍵對
應值。put方法會返回其原來的值。
*/
System.out.println(map.put("01","huangxiang"));//返回zhangsan1
//判斷是否存在改鍵。結果:containsKey:false
System.out.println("containsKey:"+map.containsKey("022"));
//刪除元素,remove(key)方法,返回刪除的鍵對應的值。
//System.out.println("remove:"+map.remove("02"));//remove:zhangsan2
//獲取指定鍵的值。
System.out.println("get:"+map.get("01"));//get:huangxiang
//如果此集合不包含該鍵的映射關係,則返回null。
map.put("04",null);
System.out.println("get:"+map.get("04"));//get:null
//獲取map集合中所有的值。
Collection<String> coll = map.values();
System.out.println(coll);//[null, huangxiang, zhangsan2, zhangsan3]
System.out.println(map);//{04=null, 01=huangxiang, 02=zhangsan2, 03=zhangsan3}
}
}
三、HashMap、Hashtable和TreeMap類
1、HashMap和Hashtable概述
HashMap和Hashtable之間的關係完全類似與ArrayList與Vector的關係。兩個底層都是哈希表數據結構,兩者的區別在於:
- Hashtable不可以存入null鍵和null值,HashMap可以存入。
- Hashtable是線程安全實現,HashMap是線程不安全的實現類,HashMap的性能可能較高點。
所以,這裏主要對HashMap進行分析。
2、TreeMap類概述
TreeMap類底層的實現是二叉樹結構。線程是不同步的,其特點是,TreeMap會對集合中的所有Key進行排序,排序的有兩種方式:
- 自然排序:TreeMap的所有key必須實現Compareable接口,而且所有key必須是同一類對象。
- 自定義比較器排序:通過自定義比較器的方式,在創建集合時,將比較器作爲對象參數傳入。
3、HashMap構造方法摘要
- HashMap():構造一個具有默認初始容量 (16) 和默認加載因子 (0.75) 的空 HashMap。
- HashMap(int initialCapacity):構造一個帶指定初始容量和默認加載因子 (0.75) 的空 HashMap。
- HashMap(int initialCapacity, float loadFactor):構造一個帶指定初始容量和加載因子的空 HashMap。
- HashMap(Map<? extendsK,? extendsV> m):構造一個映射關係與指定 Map 相同的新 HashMap。
4、TreeMap構造方法摘要
- TreeMap():使用鍵的自然順序構造一個新的、空的樹映射。
- TreeMap(Comparator<? superK> comparator):構造一個新的、空的樹映射,該映射根據給定比較器進行排序。
- TreeMap(Map<? extendsK,? extendsV> m):構造一個與給定映射具有相同映射關係的新的樹映射,該映射根據其鍵的自然順序 進行排序。
- TreeMap(SortedMap<K,? extends V> m):構造一個與指定有序映射具有相同映射關係和相同排序順序的新的樹映射。
5、Map集合的兩種取出方式
由於在Map集合中沒有Iterator迭代器,要想遍歷取出Map集合中的元素可以通過以下兩個方法:
- Set<K> keySet():將map中所有的鍵存到set集合。因爲set具有迭代器。通過迭代方式取出所有的鍵,再根據get方法,取出每一個鍵對應的值。
- Set<Map.Entry<K,V>> entrySet():將map集合中的映射關係存入到了set集合中,而這個關係的數據類型就是:Map.Entry。
Map集合兩種取出方法的代碼示例:
import java.util.*;
/*
map集合的兩種取出方式:
1、keyset:將map中所有的鍵存到set集合。因爲set具有迭代器。通過迭代方式取出所有
的鍵,再根據get方法,取出每一個鍵對應的值。
Map集合取出原理:將map集合轉成set集合。再通過迭代器取出。
2、entryset:將map集合中的映射關係存入到了set集合中,
而這個關係的數據類型就是:Map.Entry。
*/
public class MapTest2{
public static void main(String[] args){
//創建map集合
Map<String,String> map = new HashMap<String,String>();
//添加元素
map.put("01","huangxiang");
map.put("02","huangxiang2");
map.put("03","huangxiang3");
map.put("04","huangxiang4");
//將map集合中的映射關係取出,存入到Set集合中。
Set<Map.Entry<String,String>> entrySet = map.entrySet();
//返回set迭代器,通過Map.entrySet 方法返回映射的 collection 視圖。
Iterator<Map.Entry<String,String>> it = entrySet.iterator();
//迭代取出
while(it.hasNext()){
Map.Entry<String,String> me = it.next();
//通過Map.Entty接口提供的getKey()和getValue()方法取出鍵和值。
String key = me.getKey();
String value = me.getValue();
//打印輸出。
System.out.println(key+"::"+value);
}
/* 輸出結果
04::huangxiang4
01::huangxiang1
02::huangxiang2
03::huangxiang3
*/
//先獲取map集合的所有鍵的set集合
Set<String> keySet = map.keySet();
//有了set集合,就可以獲取其迭代器
Iterator<String> it2 = keySet.iterator();
//通過迭代器獲取其鍵值
while(it2.hasNext()){
String key = it2.next();
String value = map.get(key);
System.out.println("key:"+key+"::value:"+value);
}
/*輸出結果:
key:04::value:huangxiang4
key:01::value:huangxiang1
key:02::value:huangxiang2
key:03::value:huangxiang3
*/
}
}
6、HashMap應用示例
import java.util.*;
/*
練習:每一個學生都有對應的歸屬地。
學生Student,地址String
學生屬性:姓名和年齡
注意:姓名和年齡相同的視爲同一個學生,保證學生的唯一性。
1、描述學生
2、定義map容器。將學生作爲鍵,地址作爲值存入。
3、獲取map集合中的元素
*/
public class MapTest3{
public static void main(String[] args){
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("zhangsan",25),"shanghai");
hm.put(new Student("lisi",28),"beijing");
hm.put(new Student("wangwu",26),"tianjin");
hm.put(new Student("zhaoliu",23),"chengdu");
hm.put(new Student("zhouqi",35),"dalian");
//第一種取出方式 keySet
//1、通過keyset()方法獲取鍵值Student的Set集合。
Set<Student> keySet = hm.keySet();
//2、返回鍵值Student的Set集合的迭代器
Iterator<Student> it = keySet.iterator();
//迭代獲取鍵值
while(it.hasNext()){
Student stu = it.next();
//有了鍵值,通過map集合的get方法獲取與鍵值對應的value值
String addr = hm.get(stu);
System.out.println(stu+"::::"+addr);
}
//第二種取出方式:entrSet
Set<Map.Entry<Student,String>> entry = hm.entrySet();
Iterator<Map.Entry<Student,String>> iter = entry.iterator();
//3、迭代獲取Map.Entry
while(iter.hasNext()){
Map.Entry<Student,String> me = iter.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+"......."+addr);
}
}
}
//自定義學生類實現Comparable接口。
class Student implements Comparable<Student>{
private String name;
private int age;
//構造函數初始化
Student(String name,int age){
this.name = name;
this.age = age;
}
//提供獲取方法
public String getName(){
return name;
}
public int getAge(){
return age;
}
//自定義輸出內容
public String toString(){
return name+"::::"+age;
}
//複寫hashCode方法
public int hashCode(){
return name.hashCode()+age*52;
}
//複寫equals方法
public boolean equals(Object obj){
//健壯性判斷
if(!(obj instanceof Student))
throw new ClassCastException("類型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
//複寫compareTo方法
public int compareTo(Student s){
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num == 0)
return this.name.compareTo(s.name);
return num;
}
}
7、TreeMap應用舉例:對學生的年齡按照升序排列
import java.util.*;
/*
需求:對學生對象的年齡進行升序排序。
因爲數據是以鍵值對形式存在的。
所以要使用可以排序的Map集合。TreeMap。
*/
public class MapTest4{
public static void main(String[] args){
/*
創建一個TreeMap集合。可以用複寫comparable接口中的compareTo方法進行
比較排序。也可以自定義一個比較器實現comparator接口進行比較。
*/
//自定義比較器可以作爲對象參數傳入,也可以使用匿名內部類。
TreeMap<Student,String> tm = new TreeMap<Student,String>(/*new StuNameComparator()*/);
//添加元素
tm.put(new Student("zhangsan",25),"shanghai");
tm.put(new Student("lisi",28),"beijing");
tm.put(new Student("wangwu",26),"tianjin");
tm.put(new Student("zhaoliu",23),"chengdu");
tm.put(new Student("zhouqi",35),"dalian");
//entrySet取出方式
Set<Map.Entry<Student,String>> entrySet = tm.entrySet();
Iterator<Map.Entry<Student,String>> it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<Student,String> me = it.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+"....."+addr);
}
}
}
//自定義比較器:以字母的自然排序爲主。。因爲下面的學生類中實現了按年齡排序。
//所以這個比較器實現了以字母的自然順序排序。。
class StuNameComparator implements Comparator<Student>{
public int compare(Student s1,Student s2){
int num = s1.getName().compareTo(s2.getName());
if(num == 0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}
//學生類
class Student implements Comparable<Student>{
private String name;
private int age;
//構造函數初始化
Student(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String toString(){
return name+"::::"+age;
}
//複寫hashCode()方法
public int hashCode(){
return name.hashCode()+age*52;
}
//複寫equals()方法
public boolean equals(Object obj){
if(!(obj instanceof Student))
throw new ClassCastException("類型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
//複寫compareTo方法。
public int compareTo(Student s){
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num == 0)
return this.name.compareTo(s.name);
return num;
}
}
8、Map集合綜合運用舉例
何時使用Map集合:當發現存在映射關係時,我們就可以選擇Map集合。應爲Map集合其本身存儲的就是映射關係。其中HashMap是散列映射表,TreeMap是樹映射表。
練習舉例:"sdfgzxcvasdfxcvdf"獲取該字符串中的字母出現的次數。希望打印結果:a(1)c(2).....
代碼實現如下:
import java.util.*;
/*
練習:
"sdfgzxcvasdfxcvdf"獲取該字符串中的字母出現的次數。
希望打印結果:a(1)c(2).....
通過結果發現,每一個字母都有對應的次數。
說明字母和次數之間都有映射關係。
注意了,當發現有映射關係時,可以選擇map集合。
因爲map集合中存放就是映射關係。
什麼使用map集合呢?
當數據之間存在這映射關係時,就要先想map集合。
思路:
1,將字符串轉換成字符數組。因爲要對每一個字母進行操作。
2,定義一個map集合,因爲打印結果的字母有順序,所以使用treemap集合。
3,遍歷字符數組。
將每一個字母作爲鍵去查map集合。
如果返回null,將該字母和1存入到map集合中。
如果返回不是null,說明該字母在map集合已經存在並有對應次數。
那麼就獲取該次數並進行自增。,然後將該字母和自增後的次數存入到map集合中。覆蓋調用原理鍵所對應的值。
4,將map集合中的數據變成指定的字符串形式返回。
*/
public class MapTest5{
public static void main(String [] args){
String s = charCount("sdfgzxcvasdfxcvdf");
System.out.println(s);
}
public static String charCount(String str){
//將字符串轉化爲字符串數組,因爲要操作每一個字符。
char[] chs = str.toCharArray();
/*
創建一個TreeMap集合。因爲TreeMap其按鍵自動排序。將字符作爲鍵,出現次數
作爲值存入。
*/
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
int count = 0;
for(int i = 0;i < chs.length;i++){
if(chs[i] < 'a' && chs[i] > 'z' || chs[i] < 'A' && chs[i] > 'Z')
continue;//非字母,則不計
Integer value = tm.get(chs[i]);//獲取第i個角標對應的值。
//當value不爲空,用count記住其值
if(value != null)
count = value;
count++;
tm.put(chs[i],count);//直接存入,自動裝箱
count = 0;//計數器復位。不復位會出現統計錯誤。
}
//因爲要按照a(1)c(2).....格式輸出,所以建立一個容器
StringBuilder sb = new StringBuilder();
Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();
//遍歷
while(it.hasNext()){
Map.Entry<Character,Integer> me = it.next();
//用Map.Entry中的方法取出字符及其出現次數
Character ch = me.getKey();
Integer value = me.getValue();
sb.append(ch+"("+value+")");//存入容器中
}
return sb.toString();//返回字符串
}
}