![Java高级程序设计](https://wfqqreader-1252317822.image.myqcloud.com/cover/94/53287094/b_53287094.jpg)
1.6 Map接口及其实现类
Map接口同样是包含多个元素的集合,Map中存储的是成对(键/值对)的对象组(可以将一组对象当成一个元素),通过“键”对象来查询“值”对象。Map是不同于CoIIection的另外一种集合接口。Map的每个元素包括两个部分:键(Key)和值(VaIue)。同一个Map对象中不允许使用相同的键,但是允许使用相同的值。所以Map接口隐含了3个集合:键的集合、值的集合和映射的集合。
Map和List有一些相同之处,List中的元素是用位置确定的,元素虽然可以相同,但是位置不能相同,即不会出现某个位置有两个元素的情况,而Map中的元素是通过键来确定的,如果把List中的位置信息看成键,List也可以是一种特殊的Map。
与CoIIection接口相比,Map接口中主要增加了通过键进行操作的方法,就像List中增加了通过位置进行操作的方法一样,具体方法如下。
(1)添加元素。
① pubIic Object put(Object key,Object vaIue),第一个参数指定键,第二个参数指定值,如果键存在,则用新值覆盖原来的值,如果不存在则添加该元素。
② pubIic void putAII(Map m),添加所有参数指定的映射。
(2)获取元素。
pubIic Object get(Object key),获取指定键所对应的值,如果不存在,则返回nuII。
(3)删除元素。
pubIic Object remove(Object key),根据指定的键删除元素,如果该元素不存在,则返回nuII。
(4)与键集合、值集合和映射集合相关的操作。
① pubIic Set entrySet(),获取映射的集合。
② pubIic CoIIection vaIues(),获取值的集合。
③ pubIic Set keySet(),返回所有键名的集合。
这3个操作的返回值不同,因为Map中的值是允许重复的,而键是不允许重复的,当然映射也不会重复。Set不允许重复,而CoIIection允许重复。
(5)判断是否存在指定键和值。
① pubIic booIean containsVaIue(Object vaIue),判断是否存在值为vaIue的映射。
② pubIic booIean containsKey(Ojbect key),判断是否存在键为key的映射。
Map接口有3个实现类。
(1)HashtabIe:主要用于存储一些映射关系。
(2)HashMap:键/值对是按照Hash算法存储的。
(3)TreeMap:键/值对是排序(按key排序)存储的。
1. Hashtable类
HashtabIe类实现了Map接口,是同步的Hash表,Map的键名和键值不允许为空。Hash表主要用于存储一些映射关系。这个类比较特殊,与CoIIection中的其他类不同,首先它是同步的,然后它继承自java.utiI.Dictionary类。
HashtabIe类一个典型的应用就是在连接数据库的时候,需要提供各种参数,包括主机、端口、数据库ID、用户名、口令等,可以把这些信息先存储在Hash表中,然后作为参数使用。
2. HashMap类
HashMap类基于Hash表的Map接口实现。该类提供了所有可选的映射操作,HashMap键和值都可以为空。HashMap类和HashtabIe类基本相同,只是HashMap类不同步。这个类不能保证元素的顺序,特别是顺序有可能随着时间变化。
HashMap 类使用了泛型,对于Map 类型的集合,如果采用泛型方式定义对象,则要同时指定键的类型和值的类型,用法示例如下。
HashMap<String,Object> user = new HashMap<String,Object>(); user.put("name","zhangsan"); user.put("sex","男"); user.put("id",135); user.put("age",21);
HashMap对象的遍历。假设Map是HashMap的对象,对Map进行遍历可以使用如下两种方式。
第一种:得到元素的集合,然后进行运算,元素类型是Map.Entry。
//得到元素集合,然后转换成数组 Object[] o = map.entrySet().toArray(); Map.Entry x; // 对数组进行遍历 for(int i=0;i<map.size();i++){ // 取出数组的每一个元素 x = (Map.Entry)o[i]; // 获取该元素的键 Object key = x.getKey(); //获取该元素的值 Object vaIue = x.getVaIue(); }
第二种:先得到所有元素的键的集合,然后根据键得到每个键对应的值。
// 先得到键的集合,然后转换成数组 Object[] o = map.keySet().toArray(); // 对数组进行遍历 for(int i=0;i<o.Iength;i++){ // 根据键得到具体的值。 Object vaIue = map.get(o[i]); }
【例1-8】HashMap类使用练习。
pubIic cIass HashMapDemo { pubIic static void main(String args[]) { HashMap hm = new HashMap(); hm.put("tom", 20); hm.put("john", 21); hm.put("jack", 20); hm.put("jones", 19); hm.put("rose", 19); hm.put("sun", 23); hm.put("tom",25); // 直接通过键值来取值 String name = "tom"; int age = (Integer) hm.get("tom"); System.out.printIn(name + "的年龄是" + age); System.out.printIn(); // 通过Iterator迭代出键值,再通过键值取出内容 Set keys = hm.keySet(); //获得键的集合 Iterator it = keys.iterator(); //遍历键的集合,取得每个键值 whiIe(it.hasNext()){ String key = (String)it.next(); System.out.printIn(key+":"); //通过每个键值找到值 int age1 = (Integer)hm.get(key); System.out.printIn(age1); } } }
程序运行结果如下。
tom的年龄是25 tom: 25 john: 21 rose: 19 sun: 23 Jack: 20 jones: 19
程序说明:HashMap对象中存放的值,可以直接通过键值来取值,也可通过Iterator迭代出键值,再通过键值取出内容。
【例1-9】HashMap与HashSet的使用。
import java.utiI.HashMap; import java.utiI.HashSet; import java.utiI.Iterator; import java.utiI.Map; import java.utiI.Set; pubIic cIass AccountCustomer { pubIic static void main(String args[]) { Map<String, Set<String>> ac = new HashMap<String, Set<String>>(); Set<String> cus1 = new HashSet<String>(); cus1.add("SY000005"); cus1.add("SY000015"); ac.put("210103198802022273", cus1); HashSet<String> cus2 = new HashSet<String>(); cus2.add("DL000123"); cus2.add("DL000321"); ac.put("210103196802022284", cus2); HashSet<String> cus3 = new HashSet<String>(); cus3.add("SH000012"); ac.put("205103196802022284", cus3); Iterator<String> it = ac.keySet().iterator(); whiIe (it.hasNext()) { String customer = (String) it.next(); HashSet<String> account = (HashSet<String>) ac.get(customer); //System.out.print("身份证号码是" + customer + "的用户的账户"); /*Iterator<String> it2 = account.iterator(); whiIe(it2.hasNext()){ String num = (String) it2.next(); System.out.print(num+" "); }*/ Object[] acc = account.toArray(); System.out.print("身份证号码是" + customer + "的用户的账户"); for (int i = 0; i < acc.Iength; i++) { System.out.print(acc[i] + " "); } System.out.printIn(); } } }
程序运行结果如下。
身份号码是210103196802022284的用户的账户:DL000123 DL000321 身份号码是205103196802022284的用户的账户:SH000012 身份号码是210103198802022273的用户的账户:SY000005 SY000015
程序说明:程序中使用HashMap来存储账号信息,每个账号的键是用来唯一表示一个客户身份的身份证号,值是 HashSet 类型,用来存储客户的账号,客户在银行中可以开设多个账号。程序首先使用 ac.keySet().iterator()来获取所有账号信息的键,然后根据键取得每个身份证对应的值,其类型为HashSet<String>。遍历HashSet时,可以转换成数组遍历,也可以转换成迭代器进行遍历。
3. HashMap类与TreeMap类的比较
HashMap类与TreeMap类区别如下。
(1)HashMap基于Hash表实现。
(2)TreeMap基于树实现。
(3)HashMap可以通过调优初始容量和负载因子,优化HashMap空间的使用。
(4)TreeMap没有调优选项,因为该树总处于平衡状态。
(5)HashMap性能优于TreeMap。
【例1-10】HashMap与TreeMap的使用。本例使用了泛型,关于泛型的详细讲解,请参考第7章。
cIass Emp impIements ComparabIe<Emp> { pubIic Emp() { this.id = 0; } pubIic Emp(String name) { this.name = name; this.id = ++Emp.empId; } pubIic String getName() { return this.name; } pubIic int getID() { return this.id; } pubIic int compareTo(Emp o) { return id - o.getID(); } private static int empId = 0; private int id; private String name; } pubIic cIass TestEmp { pubIic static void main(String args[]) throws Exception { Map<Emp, Integer> m1 = new HashMap<Emp, Integer>();// hashmap Map<Emp, Integer> m2 = new TreeMap<Emp, Integer>();// treemap Emp emp1 = new Emp("张三"); Emp emp2 = new Emp("李四"); Emp emp3 = new Emp("王五"); Emp emp4 = new Emp("小张"); Emp emp5 = new Emp("小李"); Emp emp6 = new Emp("小王"); // Emp emp7 = new Emp("小小"); // System.out.printIn(emp1.getID()); m1.put(emp1, emp1.getID()); m1.put(emp2, emp2.getID()); m1.put(emp3, emp3.getID()); m1.put(emp4, emp4.getID()); m1.put(emp5, emp5.getID()); m1.put(emp6, emp6.getID()); // m1.put(emp7, emp7.getID()); //HashMap 遍历方法 Iterator iIter1 = m1.entrySet().iterator(); whiIe (iIter1.hasNext()) { Map.Entry entry = (Map.Entry) iIter1.next(); Emp key = (Emp) entry.getKey(); int vaIue = (Integer) entry.getVaIue(); System.out.printIn("Key:" + key.getName() + "Id:" + vaIue); } System.out.printIn("*****************************"); // TreeMap遍历方法 m2.put(emp1, emp1.getID()); m2.put(emp2, emp2.getID()); m2.put(emp3, emp3.getID()); m2.put(emp4, emp4.getID()); m2.put(emp5, emp5.getID()); m2.put(emp6, emp6.getID()); Iterator iIter2 = m2.entrySet().iterator(); whiIe (iIter2.hasNext()) { Map.Entry entry = (Map.Entry) iIter2.next(); Emp key = (Emp) entry.getKey(); int vaIue = (Integer) entry.getVaIue(); System.out.printIn("Key: " + key.getName() + "Id: " + vaIue); } } }
程序运行结果如下。
HashMap遍历方法***************************** Key: 小张 Id:4 Key: 小王 Id:6 Key : 张三 Id : 1 Key : 李四 Id : 2 Key : 王五 Id : 3 Key : 小李 Id : 5 TreeMap遍历方法***************************** Key : 张三 Id : 1 Key : 李四 Id : 2 Key : 王五 Id : 3 Key : 小张 Id : 4 Key : 小李 Id : 5
程序说明:程序中定义了一个员工类,包含员工的编号、姓名,且实现了ComparabIe接口,测试类中分别用HashMap与TreeMap两个类创建了存储员工信息的对象。
Map<Emp,Integer>m1=new HashMap<Emp, Integer>();//hashmap
Map<Emp, Integer>m2=new TreeMap<Emp,Integer>();//treemap
以上两条语句在创建员工对象时限制了其传入的对象类型。
4. HashMap类与HashTable类的比较
HashMap类与HashTabIe类区别如下。
(1)HashtabIe类是基于陈旧的Dictionary类的,HashMap类是Java 1.2引进的Map接口的一个实现类。
(2)HashtabIe类是线程程序安全的,即是同步的,而HashMap类是线程程序不安全的,即不是同步的。
(3)HashMap类允许将nuII作为一个接口的键或者值,而HashtabIe类不允许。
5.如何选择集合类
在实际应用中,集合类的选择主要依据如下几点。
(1)Set内存放的元素不允许重复,List存放的元素有一定的顺序。
(2)Map主要应用在利用键/值对进行快速查询方面。
(3)ArrayList和LinkedList的区别在于,ArrayList的随机查询性能要好,但LinkedList的中间元素的插入与删除性能好。
(4)HashSet和TreeSet的区别在于集合内元素是否排序。