集是一个集合,它可以快速地寻找现有的元素,但是要查看元素,就需要查看的元素的精确副本。这不是一种非常通用的查找方式。通常,我们知道某些键的信息,并想要查找与之相对应的元素。映射表(map)数据结构就是为此设计的。映射表用来存放键值对。如果提供了键就能查找到相应的值。例如,有一张关于员工信息的记录表,键为员工ID,值为Employee对象。
Java 类库为映射表提供了两种通用的实现:HashMap和TreeMap。这两个类都实现了Map接口。
散列映射表对键进行散列,树映射表用键的整体顺序对元素进行排序,并将其组织成为搜索树。散列或者比较函数只能用于键。与键关联的值不能用来散列或者比较。
应该选择散列映射表呢?还是树映射表呢?与集一样,散列稍微地快一些,如果不需要按照排列顺序访问,就最好使用散列。
下列代码为存储的员工信息建立了一个散列映射表:
Map<String, Employee> staff = new HashMap<String, Employee>();
Employee Harry = new Employee("Harry Hacker");
staff.put("987-98-9996");
每当往映射表中添加对象时,必须同时提供一个键。在这里键是一个字符串,对应的值是一个Employee对象。
想要检索一个对象,必须提供一个键。
String s = "987-98-9996";
e = staff.get(s);
如果在映射表中没有给定键对应的信息,那么get将会返回一个null。
键必须是唯一的。不能对同一个键存放两个值。如果对同一个键两次调用put方法,第二个值就会取代第一个值。实际上,put将返回用这个键参数存储的上一个值。
remove方法用于从映射表中删除给定键对应的元素。size方法用于返回映射表中的元素数。
集合框架并没有将映射表本身视为一个集合(其他的数据结构框架则将映射表视为对(pairs)或者视为用键作为索引值的集合)。然而,可以获得映射表的视图,这是一组实现Collection接口的对象,或者它的子接口视图。
有三个视图,他们分别是:键集、值集合(不是集)和键/值对集。键与键值对形成了一个集,这是因为在映射表中一个键只能有一个副本。下列的方法将返回这3个视图(条目集的元素是静态内部类Map.Entry的对象)。
Set<K> keySet()
Collection<K> values ()
Set <Map.Entry<K,V>> entrySet()
注意KeySet既不是HashSet,也不是TreeSet,而是实现了Set接口的某个其他类的对象。Set接口扩展了collection接口,因此可以与使用任何集合一样使用KeySet。
例如可以枚举映射表中的所有键:
Set<String> keys = map.keySet();
for(String key :keys)
{
do something with the keys
}
提示:如果想要同时查看键与值,就可以枚举各个条目(entires)查看,以避免对值进行查找。 可以使用下面这段代码框架:
for(Map.Entry<String,Employee> entry:staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
do something with the key,value
}
如果调用迭代器的remove方法,实际上就从映射表中删除了键以及对应的值。但是不能将元素添加到键集的视图中。如果只添加键不添加值是毫无意义的,如果试图调用add方法,会抛出一个UnsupportedOperationException异常。条目集视图也有同样的限制,不过从概念上讲,添加新的键值对是有意义的。
下面的代码显示了映射表的整个操作过程。首先将键值对添加到映射表中,然后从映射表中删除一个键,同时与之对应的值也被删除了。接下来修改与某一个键对应的值,并调用get方法查看这个值。最后对条目集进行迭代。
import java.util.*;
/**
* This program demonstrates the use of a map with key type String and value type Employee.
* @version 1.10 2004-08-02
* @author Cay Horstmann
*/
public class MapTest
{
public static void main(String[] args)
{
Map<String, Employee> staff = new HashMap<String, Employee>();
staff.put("144-25-5464", new Employee("Amy Lee"));
staff.put("567-24-2546", new Employee("Harry Hacker"));
staff.put("157-62-7935", new Employee("Gary Cooper"));
staff.put("456-62-5527", new Employee("Francesca Cruz"));
// print all entries
System.out.println(staff);
// remove an entry
staff.remove("567-24-2546");
// replace an entry
staff.put("456-62-5527", new Employee("Francesca Miller"));
// look up a value
System.out.println(staff.get("157-62-7935"));
// iterate through all entries
for (Map.Entry<String, Employee> entry : staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
System.out.println("key=" + key + ", value=" + value);
}
}
}
/**
* A minimalist employee class for testing purposes.
*/
class Employee
{
/**
* Constructs an employee with $0 salary.
* @param n the employee name
*/
public Employee(String n)
{
name = n;
salary = 0;
}
public String toString()
{
return "[name=" + name + ", salary=" + salary + "]";
}
private String name;
private double salary;
}