Map是一种很重要的数据结构。在本文中,我会告诉你们如何使用HashMap、TreeMap、Hashtable、LinkedHashMap这四种map。
1. Map概览
在Java SE中,Map有四种常用的实现:HashMap、TreeMap、Hashtable和LinkedHashMap。我们可以使用一句话来分别描述各种实现,如下:
① HashMap是作为哈希表(hash table)实现的,其中的键(key)和值(value)没有顺序。
② TreeMap是基于红黑树(red-black tree)结构实现的,其中的元素根据键(key)进行排序。
③ LinkedHashMap保留了元素的插入顺序。
④ Hashtable是同步的,这一点和HashMap不同。
因此,在线程安全(thread-safe)的情况下,应使用HashMap,因为Hashtable需要额外的同步开销。
2. HashMap
如果HashMap中的键(key)是自定义的对象,那么该对象需要遵循equals()和hashCode()协议。
class Dog {
String color;
Dog(String c) {
color = c;
}
public String toString(){
return color + " dog";
}
}
public class TestHashMap {
public static void main(String[] args) {
HashMap<Dog, Integer> hashMap = new HashMap<Dog, Integer>();
Dog d1 = new Dog("red");
Dog d2 = new Dog("black");
Dog d3 = new Dog("white");
Dog d4 = new Dog("white");
hashMap.put(d1, 10);
hashMap.put(d2, 15);
hashMap.put(d3, 5);
hashMap.put(d4, 20);
//print size
System.out.println(hashMap.size());
//loop HashMap
for (Entry<Dog, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey().toString() + " - " + entry.getValue());
}
}
}
输出结果:
4
white dog – 5
black dog – 15
red dog – 10
white dog – 20
这里请注意,我们添加两次”white dogs“,这本该是个错误,但HashMap却照常执行。这就有点儿说不通了,我们现在不知道到底有多少”white dogs“。
Dog类应该定义如下:
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color == this.color;
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
现在输出的结果是:
3
red dog – 10
white dog – 20
black dog – 15
究其原因,HashMap中不允许存在两个完全相同的元素。默认的,Object类中的hashCode()和equals()方法会被调用。默认的hashCode()方法为不同的对象分配不同的增速,而当两个变量引用同一个对象时,equals()方法返回true。如果你对此了解的不是特别清楚,请查看文章
《the hashCode() and equals() contract》。
查看文章《the most frequently used methods for HashMap》,如iteration、print等等。
3. TreeMap
TreeMap按键(key)对元素进行排序。为了理解“按key排序”的概念,我们首先看一下下面的例子:
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color == this.color;
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
public class TestTreeMap {
public static void main(String[] args) {
Dog d1 = new Dog("red");
Dog d2 = new Dog("black");
Dog d3 = new Dog("white");
Dog d4 = new Dog("white");
TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
treeMap.put(d1, 10);
treeMap.put(d2, 15);
treeMap.put(d3, 5);
treeMap.put(d4, 20);
for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
}
}
输出结果:
Exception in thread “main” java.lang.ClassCastException: collection.Dog cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(Unknown Source)
at collection.TestHashMap.main(TestHashMap.java:35)
由于TreeMap是按照键(key)进行排序的,用来当做键的对象相互之间必须能够进行大小比较,所以它必须实现Comparable接口。例如,你可以将String作为键,因为String实现了Comparable接口。
我们来修改一下Dog类,使它可以进行大小比较。
class Dog implements Comparable<Dog>{
String color;
int size;
Dog(String c, int s) {
color = c;
size = s;
}
public String toString(){
return color + " dog";
}
@Override
public int compareTo(Dog o) {
return o.size - this.size;
}
}
public class TestTreeMap {
public static void main(String[] args) {
Dog d1 = new Dog("red", 30);
Dog d2 = new Dog("black", 20);
Dog d3 = new Dog("white", 10);
Dog d4 = new Dog("white", 10);
TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
treeMap.put(d1, 10);
treeMap.put(d2, 15);
treeMap.put(d3, 5);
treeMap.put(d4, 20);
for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
}
}
输出结果:
red dog – 10
black dog – 15
white dog – 20
在本例中,TreeMap是按照键dog size进行排序的。
如果“Dog d4 = new Dog("white", 10);”被替换为“Dog d4 = new Dog("white", 40);”,那么输出的结果会变成:
white dog – 20
red dog – 10
black dog – 15
white dog – 5
因为TreeMap使用compareTo()方法来比较键,因此不同size的dog被认为是不相同的。
4. Hashtable
来自Java文档:HashMap类与Hashtable基本相同,但HashMap不是同步的,而且允许空值作为key或value。
5. LinkedHashMap
LinkedHashMap是HashMap的子类,这意味着它继承了HashMap的特性。而且,LinkedHashMap中的链表确保了元素的插入有序。
我们将上面HashMap的示例程序中的HashMap替换为LinkedHashMap。
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color == this.color;
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
public class TestHashMap {
public static void main(String[] args) {
Dog d1 = new Dog("red");
Dog d2 = new Dog("black");
Dog d3 = new Dog("white");
Dog d4 = new Dog("white");
LinkedHashMap<Dog, Integer> linkedHashMap = new LinkedHashMap<Dog, Integer>();
linkedHashMap.put(d1, 10);
linkedHashMap.put(d2, 15);
linkedHashMap.put(d3, 5);
linkedHashMap.put(d4, 20);
for (Entry<Dog, Integer> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
}
}
输出结果:
red dog – 10
black dog – 15
white dog – 20
如果我们使用HashMap的话,由于不能保留元素插入的顺序,因此输出的结果也不同,如下:
red dog – 10
white dog – 20
black dog – 15