Map是最重要的数据结构。这篇文章中,我会带你们看看HashMap, TreeMap, HashTable和LinkedHashMap的区别。
Java SE中有四种常见的Map实现——HashMap, TreeMap, Hashtable和LinkedHashMap。如果我们使用一句话来分别概括它们的特点,就是:
HashMap就是一张hash表,键和值都没有排序。
TreeMap以红-黑树结构为基础,键值按顺序排列。
LinkedHashMap保存了插入时的顺序。
Hashtable是同步的(而HashMap是不同步的)。所以如果在线程安全的环境下应该多使用HashMap,而不是Hashtable,因为Hashtable对同步有额外的开销。
如果HashMap的键(key)是自定义的对象,那么需要按规则定义它的equals()和hashCode()方法。
1.class Dog { 2. String color; 3. 4. Dog(String c) { 5. color = c; 6. } 7. public String toString(){ 8. return color + " dog"; 9. } 10.} 11. 12.public class TestHashMap { 13. public static void main(String[] args) { 14. HashMap hashMap = new HashMap(); 15. Dog d1 = new Dog("red"); 16. Dog d2 = new Dog("black"); 17. Dog d3 = new Dog("white"); 18. Dog d4 = new Dog("white"); 19. 20. hashMap.put(d1, 10); 21. hashMap.put(d2, 15); 22. hashMap.put(d3, 5); 23. hashMap.put(d4, 20); 24. 25. //print size 26. System.out.println(hashMap.size()); 27. 28. //loop HashMap 29. for (Entry entry : hashMap.entrySet()) { 30. System.out.println(entry.getKey().toString() + " - " + entry.getValue()); 31. } 32. } 33.}
输出:
1.4 2.white dog - 5 3.black dog - 15 4.red dog - 10 white dog - 20
注意,我们错误的将”white dogs”添加了两次,但是HashMap却接受了两只”white dogs”。这不合理(因为HashMap的键不应该重复),我们会搞不清楚真正有多少白色的狗存在。
Dog类应该定义如下:
1.class Dog { 2. String color; 3. 4. Dog(String c) { 5. color = c; 6. } 7. 8. public boolean equals(Object o) { 9. return ((Dog) o).color == this.color; 10. } 11. 12. public int hashCode() { 13. return color.length(); 14. } 15. 16. public String toString(){ 17. return color + " dog"; 18. } 19.}
现在输出结果如下:
1.3 2.red dog - 10 3.white dog - 20 4.black dog - 15
输出结果如上是因为HashMap不允许有两个相等的元素存在。默认情况下(也就是类没有实现hashCode()和equals()方法时),会使用Object类中的这两个方法。Object类中的hashCode()对于不同的对象会返回不同的整数,而只有两个引用指向的同样的对象时equals()才会返回true。如果你不是很了解hashCode()和equals()的规则,可以看看这篇文章。
来看看HashMap最常用的方法,如迭代、打印等。
TreeMap的键按顺序排列。让我们先看个例子看看什么叫作“键按顺序排列”。
1.class Dog { 2. String color; 3. 4. Dog(String c) { 5. color = c; 6. } 7. public boolean equals(Object o) { 8. return ((Dog) o).color == this.color; 9. } 10. 11. public int hashCode() { 12. return color.length(); 13. } 14. public String toString(){ 15. return color + " dog"; 16. } 17.} 18. 19.public class TestTreeMap { 20. public static void main(String[] args) { 21. Dog d1 = new Dog("red"); 22. Dog d2 = new Dog("black"); 23. Dog d3 = new Dog("white"); 24. Dog d4 = new Dog("white"); 25. 26. TreeMap treeMap = new TreeMap(); 27. treeMap.put(d1, 10); 28. treeMap.put(d2, 15); 29. treeMap.put(d3, 5); 30. treeMap.put(d4, 20); 31. 32. for (Entry entry : treeMap.entrySet()) { 33. System.out.println(entry.getKey() + " - " + entry.getValue()); 34. } 35. } }
输出:
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按照键的顺序进行排列对象,所以键的对象之间需要能够比较,所以就要实现Comparable接口。你可以使用String作为键,String已经实现了Comparable接口。
我们来修改下Dog类,让它实现Comparable接口
1.class Dog implements Comparable<Dog>{ 2. String color; 3. int size; 4. 5. Dog(String c, int s) { 6. color = c; 7. size = s; 8. } 9. 10. public String toString(){ 11. return color + " dog"; 12. } 13. 14. @Override 15. public int compareTo(Dog o) { 16. return o.size - this.size; 17. } 18.} 19. 20.public class TestTreeMap { 21. public static void main(String[] args) { 22. Dog d1 = new Dog("red", 30); 23. Dog d2 = new Dog("black", 20); 24. Dog d3 = new Dog("white", 10); 25. Dog d4 = new Dog("white", 10); 26. 27. TreeMap treeMap = new TreeMap(); 28. treeMap.put(d1, 10); 29. treeMap.put(d2, 15); 30. treeMap.put(d3, 5); 31. treeMap.put(d4, 20); 32. 33. for (Entry entry : treeMap.entrySet()) { 34. System.out.println(entry.getKey() + " - " + entry.getValue()); 35. } 36. } 37.}
输出:
1.red dog - 10 2.black dog - 15 3.white dog - 20
结果根据键的排列顺序进行输出,在我们的例子中根据size排序的。
如果我们将“Dog d4 = new Dog(“white”, 10);”替换成“Dog d4 = new Dog(“white”, 40);”,那么输出会变成:
1.white dog - 20 2.red dog - 10 3.black dog - 15 4.white dog - 5
这是因为TreeMap使用compareTo()方法来比较键值的大小,size不相等的狗是不同的狗。
Java文档写道:
HashMap类和Hashtable类几乎相同,不同之处在于HashMap是不同步的,也允许接受null键和null值。
LinkedHashMap is a subclass of HashMap. That means it inherits the features of HashMap. In addition, the linked list preserves the insertion-order.
Let’s replace the HashMap with LinkedHashMap using the same code used for HashMap.
LinkedHashMap是HashMap的子类,所以LinkedHashMap继承了HashMap的一些属性,它在HashMap基础上增加的特性就是保存了插入对象的顺序。
1.class Dog { 2. String color; 3. 4. Dog(String c) { 5. color = c; 6. } 7. 8. public boolean equals(Object o) { 9. return ((Dog) o).color == this.color; 10. } 11. 12. public int hashCode() { 13. return color.length(); 14. } 15. 16. public String toString(){ 17. return color + " dog"; 18. } 19.} 20. 21.public class TestHashMap { 22. public static void main(String[] args) { 23. 24. Dog d1 = new Dog("red"); 25. Dog d2 = new Dog("black"); 26. Dog d3 = new Dog("white"); 27. Dog d4 = new Dog("white"); 28. 29. LinkedHashMap linkedHashMap = new LinkedHashMap(); 30. linkedHashMap.put(d1, 10); 31. linkedHashMap.put(d2, 15); 32. linkedHashMap.put(d3, 5); 33. linkedHashMap.put(d4, 20); 34. 35. for (Entry entry : linkedHashMap.entrySet()) { 36. System.out.println(entry.getKey() + " - " + entry.getValue()); 37. } 38. } 39.}
输出:
1.red dog - 10 2.black dog - 15 white dog - 20
如果我们使用HashMap的话,输出将会如下,会打乱插入的顺序:
1.red dog - 10 2.white dog - 20 3.black dog - 15
java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是《HashMap》 《Hashtable》 《LinkedHashMap》 和 《TreeMap》.
Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。
Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
Hashtable与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。
LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.
HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL。
HashMap不支持线程同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致性。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
Hashtable与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtable在写入时会比较慢。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。
在遍历的时候会比HashMap慢TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iterator遍历TreeMap时,得到的记录是排过序的.