Java高级程序设计
上QQ阅读APP看书,第一时间看更新

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的区别在于集合内元素是否排序。