map是最重要的数据结构之一。在这篇文章中,我会向你展示如:HashMap, TreeMap, HashTable 和 LinkedHashMap的使用以及它们的不同。
1. Map概述
HashMap, TreeMap, Hashtable 和 LinkedHashMap在Java SE中通常有4个相同实现点。简单一句话描述如下:
HashMap 是哈希列表的一个实现,它并没有针对key或者值进行排序。
TreeMap 是基于红黑树的一个实现,它针对key进行了排序。
LinkedHashMap 保留了插入顺序。
Hashtable 是同步的, 功能同HashMap。
这就告诉我们如果是线程安全的,我们就应该使用HashMap,因为Hashtable同步是回增加开销的。
HashMap 如果HashMap 的key是自定义对象,遵循规范应该重写 equals() 和 hashCode() 方法。看如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 );
System.out.println(hashMap.size());
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” 2次,但是HashMap 同样保存了它。这是没有意义的,因为现在我们都搞不清楚了,白色的狗到底有多少是真的存在的。
正确的写法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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不允许两个相同主键的元素。默认情况下,hashCode()和equals()方法在Object类中实现使用。默认的hashCode()方法用不同的整数来区别对象,这时只有当两个对象指向同一个引用,equals() 才会返回true。如果你不是很了解,请了解hashCode() 和 equals() 。
了解常用的HashMap方法 ,例如:迭代,打印等等。
3. TreeMap TreeMap是通过key来排序的。让我们先来看看下面的例子就明白了“根据key来排序”的想法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)
因为TreeMaps 根据 key值来排序,作为key的对象相互之间必须是可比较的,这就是为什么要实现 Comparable接口。比如你用String作为key,String是实现了 Comparable接口的。
好,没我们改变Dog类,实现 Comparable接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
它由key来排序,这里就是根据dog的数量来排序。
如果使用 “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()方法对键进行比较。不同大小被识别为不同的dog!
4. Hashtable 在 Java Doc写到: HashMap大致相当于Hashtable,不同之处在于HashMap是不同步的,并且允许null值。
5. LinkedHashMap LinkedHashMap 是 HashMap 的一个子类。这就意味着LinkedHashMap 继承了 HashMap 的所有功能。额外的链表插入是有序的。
我们把上面的HashMap的例子改成LinkedHashMap:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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和LinkedHashMap 所不同的是,插入顺序不保留。输出如下:
red dog - 10
white dog - 20
black dog - 15
了解一下ArrayList vs. LinkedList vs. Vector
参考文档