如果您想了解HashMap、HashTable、LinkedHashMap和TreeMap用法和区别和hashmap,linkedmap,treemap的区别的知识,那么本篇文章将是您的不二之选。我们
如果您想了解HashMap、HashTable、LinkedHashMap和TreeMap用法和区别和hashmap,linkedmap,treemap的区别的知识,那么本篇文章将是您的不二之选。我们将深入剖析HashMap、HashTable、LinkedHashMap和TreeMap用法和区别的各个方面,并为您解答hashmap,linkedmap,treemap的区别的疑在这篇文章中,我们将为您介绍HashMap、HashTable、LinkedHashMap和TreeMap用法和区别的相关知识,同时也会详细的解释hashmap,linkedmap,treemap的区别的运用方法,并给出实际的案例分析,希望能帮助到您!
本文目录一览:- HashMap、HashTable、LinkedHashMap和TreeMap用法和区别(hashmap,linkedmap,treemap的区别)
- CurrentHashMap、HashMap、HashTable的区别
- hashmap hashtable LinkedHashMap TreeMap
- HashMap vs TreeMap vs Hashtable vs LinkedHashMap
- HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa
HashMap、HashTable、LinkedHashMap和TreeMap用法和区别(hashmap,linkedmap,treemap的区别)
Java为数据结构中的映射定义了一个接口java.util.Map,它有四个实现类,分别是HashMap、HashTable、LinkedHashMap和TreeMap。本节实例主要介绍这4中实例的用法和区别。
关键技术剖析:
Map用于存储键值对,根据键得到值,因此不允许键重复,值可以重复。
l (1)HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为null,不允许多条记录的值为null。HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要同步,可以用Collections.synchronizedMap(HashMap map)方法使HashMap具有同步的能力。
l (2)Hashtable与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,然而,这也导致了Hashtable在写入时会比较慢。
l (3)LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢。有HashMap的全部特性。
l (4)TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iteraor遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class TestMap {
public static void init(Map map){
if (map != null){
String key = null;
for (int i=5; i>0; i--){
key = new Integer(i).toString() + ".0";
map.put(key, key.toString());
//Map中的键是不重复的,如果插入两个键值一样的记录,
//那么后插入的记录会覆盖先插入的记录
map.put(key, key.toString() + "0"); }
}
}
public static void output(Map map){
if (map != null){
Object key = null;
Object value = null;
//使用迭代器遍历Map的键,根据键取值
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
//或者使用迭代器遍历Map的记录Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//一个Map.Entry代表一条记录
entry = (Map.Entry)it.next();
//通过entry可以获得记录的键和值
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
}
}
}
public static boolean containsKey(Map map, Object key){
if (map != null){
return map.containsKey(key);
}
return false;
}
public static boolean containsValue(Map map, Object value){
if (map != null){
return map.containsValue(value);
}
return false;
}
public static void testHashMap(){
Map myMap = new HashMap();
init(myMap);
//HashMap的键可以为null
myMap.put(null,"ddd");
//HashMap的值可以为null
myMap.put("aaa", null);
output(myMap);
}
public static void testHashtable(){
Map myMap = new Hashtable();
init(myMap);
//Hashtable的键不能为null
//myMap.put(null,"ddd");
//Hashtable的值不能为null
//myMap.put("aaa", null);
output(myMap);
}
public static void testLinkedHashMap(){
Map myMap = new LinkedHashMap();
init(myMap);
//LinkedHashMap的键可以为null
myMap.put(null,"ddd");
myMap.put(null,"aaa");
//LinkedHashMap的值可以为null
myMap.put("aaa", null);
output(myMap);
}
public static void testTreeMap(){
Map myMap = new TreeMap();
init(myMap);
//TreeMap的键不能为null
//myMap.put(null,"ddd");
//TreeMap的值不能为null
//myMap.put("aaa", null);
output(myMap);
}
public static void main(String[] args) {
System.out.println("采用HashMap");
TestMap.testHashMap();
System.out.println("采用Hashtable");
TestMap.testHashtable();
System.out.println("采用LinkedHashMap");
TestMap.testLinkedHashMap();
System.out.println("采用TreeMap");
TestMap.testTreeMap();
Map myMap = new HashMap();
TestMap.init(myMap);
System.out.println("新初始化一个Map: myMap");
TestMap.output(myMap);
//清空Map
myMap.clear();
System.out.println("将myMap clear后,myMap空了么? " + myMap.isEmpty());
TestMap.output(myMap);
myMap.put("aaa", "aaaa");
myMap.put("bbb", "bbbb");
//判断Map是否包含某键或者某值
System.out.println("myMap包含键aaa? "+ TestMap.containsKey(myMap, "aaa"));
System.out.println("myMap包含值aaaa? "+ TestMap.containsValue(myMap, "aaaa"));
//根据键删除Map中的记录
myMap.remove("aaa");
System.out.println("删除键aaa后,myMap包含键aaa? "+ TestMap.containsKey(myMap, "aaa"));
//获取Map的记录数
System.out.println("myMap包含的记录数: " + myMap.size());
}
}
输出结果:
采用HashMap
key: null; value: ddd
key: 3.0; value: 3.00
key: aaa; value: null
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用Hashtable
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 3.0; value: 3.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用LinkedHashMap
key: 5.0; value: 5.00
key: 4.0; value: 4.00
key: 3.0; value: 3.00
key: 2.0; value: 2.00
key: 1.0; value: 1.00
key: null; value: aaa
key: aaa; value: null
采用TreeMap
key: 1.0; value: 1.00
key: 2.0; value: 2.00
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 5.0; value: 5.00
新初始化一个Map: myMap
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
将myMap clear后,myMap空了么? true
myMap包含键aaa? true
myMap包含值aaaa? true
删除键aaa后,myMap包含键aaa? false
myMap包含的记录数: 1
源码分析:
遍历Map有两种方法:
(1)map的keySet()方法获得键的集合,再调用键集合的iterator方法获得键的迭代器,以此迭代地取出Map中的键,用get方法获得键对应的值,便完成了Map的遍历。代码如下所示:
//使用迭代器遍历Map的键,根据键取值
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
(2)使用Map的entrySet方法获得Map中记录的集合,每条对象都是一个Map.Entry对象,使用其getKey方法获得记录的键,使用其getValue方法获得记录的值。代码如下所示:
//或者使用迭代器遍历Map的记录Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//一个Map.Entry代表一条记录
entry = (Map.Entry)it.next();
//通过entry可以获得记录的键和值
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
CurrentHashMap、HashMap、HashTable的区别
HashTable
- 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
- 初始size为11,扩容:newsize = olesize*2+1
- 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
- 底层数组+链表实现,可以存储null键和null值,线程不安全
- 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
- 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
- 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
- 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
- 计算index方法:index = hash & (tab.length – 1)
HashMap的初始值还要考虑加载因子:
- 哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
- 加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
- 空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。
HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:
- 容量(capacity):hash表中桶的数量
- 初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
- 尺寸(size):当前hash表中记录的数量
- 负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)
除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。
HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。
“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:
- 较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)
- 较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销
程序猿可以根据实际情况来调整“负载极限”值。
ConcurrentHashMap
- 底层采用分段的数组+链表实现,线程安全
- 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
- Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
- 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
- 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。
在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。
Hashtable是线程安全的,它的方法是同步的,可以直接用在多线程环境中。而HashMap则不是线程安全的,在多线程环境中,需要手动实现同步机制。
Hashtable与HashMap另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。
先看一下简单的类图:
从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。
ConcurrentHashMap是使用了锁分段技术来保证线程安全的。
锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。
ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
hashmap hashtable LinkedHashMap TreeMap
hashmap:
线程不安全
并发存数据时,resize时可能会死循环。
hashtable
性能参数:
初始容量 : 预设值, 避免或减少reSize
负载因子:reSize的时机。 时间与空间的选择。 负载因子小 元素同位置的概率低,查找时间少,但空间利用率差。 负载因子大 元素同位置的概率高,查找时间多,但空间利用率高。
关于1.8的优化
对于重复index的元素,还是链表存储,但当链表长度超过8后,改为用红黑树存储。
CurrentHashMap
jdk1.8版本做了优化 采用 node+cas+ 实现高性能读写的
LinkedHashMap
在HashMap的基础上 为每个元素 增加了两个变量来表明插入数据前后节点。
TreeMap
按照key排序的Map, 默认按照升序。 可重写排序规则。
HashMap vs TreeMap vs Hashtable vs LinkedHashMap
这篇文章主要介绍了HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
Map是一个重要的数据结构,本篇文章将介绍如何使用不同的Map,如HashMap,TreeMap,HashTable和LinkedHashMap。
Map概览
Java中有四种常见的Map实现,HashMap,TreeMap,HashTable和LinkedHashMap,我们可以使用一句话来描述各个Map,如下:
HashMap:基于散列表实现,是无序的;TreeMap:基于红黑树实现,按Key排序;LinkedHashMap:保存了插入顺序;Hashtable:是同步的,与HashMap类似;HashMap
如果HashMap的Key是自己定义的对象,那么一般需要覆盖equals()和hashCode()方法,且要遵循他们之间的约定。
package simplejava; import java.util.HashMap; import java.util.Map.Entry; class Dog { String color; Dog(String c) { color = c; } public String toString() { return color + " dog"; } } public class Q26 { public static void main(String[] args) { HashMap hashMap = new HashMap(); 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 entry : hashMap.entrySet()) { System.out.println(entry.getKey().toString() + " - " + entry.getValue()); } } }
结果输出:
4
white dog - 5
red dog - 10
white dog - 20
black dog - 15
注意,我们不小心添加了两个"white dogs“,但是HashMap仍然存储它。这是不合理的,现在我们困惑到底有多少条白狗存入了HashMap,5还是20呢。
其实,Dog类应该是这样定义的:
class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(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。如果你不是很清楚,可以自己检验下hashCode()和equals()之间的关系。
举个例子,可以检验下HashMap中最常用的方法,如iteration,print等。
TreeMap
TreeMap是按key排序的,让我们先看下如下代码,了解其“按key排序”思想。
package simplejava; import java.util.Map.Entry; import java.util.TreeMap; class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(this.color); } public int hashCode() { return color.length(); } public String toString() { return color + " dog"; } } public class Q26 { 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 treeMap = new TreeMap(); treeMap.put(d1, 10); treeMap.put(d2, 15); treeMap.put(d3, 5); treeMap.put(d4, 20); for (Entry entry : treeMap.entrySet()) { System.out.println(entry.getKey() + " - " + entry.getValue()); } } }
结果输出:
Exception in thread "main" java.lang.classCastException: simplejava.Dog cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at simplejava.Q26.main(Q26.java:34)
由于TreeSet是基于Key排序的,所以作为key的对象需要相互比较,这就是为什么key需要实现Comparable接口。举个例子,你可以使用字符串作为Key,因为String已经实现了Comparable接口。
现在,让我们改变一下Dog,让它可比较,如下:
package simplejava; import java.util.Map.Entry; import java.util.TreeMap; class Dog implements Comparable { 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 Q26 { 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 treeMap = new TreeMap(); treeMap.put(d1, 10); treeMap.put(d2, 15); treeMap.put(d3, 5); treeMap.put(d4, 20); for (Entry entry : treeMap.entrySet()) { System.out.println(entry.getKey() + " - " + entry.getValue()); } } }
结果打印:
red dog - 10
black dog - 15
white dog - 20
在这个例子中,我们是按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()去比较key,不同的size意味着不同的dogs。
HashTable
参考java文档:HashMap与HashTable基本类似,除了非同步和允许null外。
LinkedHashMap
LinkedHashMap是HashMap的子类,意味着它继承了HashMap的特性,除此之外,LinkedHashMap还保存了插入顺序。
让我们使用同样的代码,然后将HashMap替换成LinkedHashMap,如下:
package simplejava; import java.util.LinkedHashMap; import java.util.Map.Entry; class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(this.color); } public int hashCode() { return color.length(); } public String toString() { return color + " dog"; } } public class Q26 { 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 linkedHashMap = new LinkedHashMap(); linkedHashMap.put(d1, 10); linkedHashMap.put(d2, 15); linkedHashMap.put(d3, 5); linkedHashMap.put(d4, 20); for (Entry 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
更多阅读:ArrayList vs. LinkedList vs. Vector
译文链接:http://www.programcreek.com/2013/03/hashmap-vs-treemap-vs-hashtable-vs-linkedhashmap/
总结
以上所述是小编给大家介绍的HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相关知识,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa
Map是最重要的数据结构。这篇文章中,我会带你们看看HashMap, TreeMap, HashTable和LinkedHashMap的区别。
1. Map概览
Java SE中有四种常见的Map实现——HashMap, TreeMap, Hashtable和LinkedHashMap。如果我们使用一句话来分别概括它们的特点,就是:
- HashMap就是一张hash表,键和值都没有排序。
- TreeMap以红-黑树结构为基础,键值按顺序排列。
- LinkedHashMap保存了插入时的顺序。
- Hashtable是同步的(而HashMap是不同步的)。所以如果在线程安全的环境下应该多使用HashMap,而不是Hashtable,因为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
|
classDog {
String color;
Dog(String c) {
color = c;
}
publicString toString(){
returncolor +" dog";
}
}
publicclassTestHashMap {
publicstaticvoidmain(String[] args) {
HashMap hashMap =newHashMap();
Dog d1 =newDog("red");
Dog d2 =newDog("black");
Dog d3 =newDog("white");
Dog d4 =newDog("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 entry : hashMap.entrySet()) {
System.out.println(entry.getKey().toString() +" - "+ entry.getValue());
}
}
}
|
输出:
1
2
3
4
5
|
4
white dog - 5
black dog - 15
red dog - 10
white dog - 20
|
注意,我们错误的将”white dogs”添加了两次,但是HashMap却接受了两只”white dogs”。这不合理(因为HashMap的键不应该重复),我们会搞不清楚真正有多少白色的狗存在。
Dog类应该定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
classDog {
String color;
Dog(String c) {
color = c;
}
publicbooleanequals(Object o) {
return((Dog) o).color ==this.color;
}
publicinthashCode() {
returncolor.length();
}
publicString toString(){
returncolor +" dog";
}
}
|
现在输出结果如下:
1
2
3
4
|
3
red dog - 10
white dog - 20
black dog - 15
|
输出结果如上是因为HashMap不允许有两个相等的元素存在。默认情况下(也就是类没有实现hashCode()和equals()方法时),会使用Object类中的这两个方法。Object类中的hashCode()对于不同的对象会返回不同的整数,而只有两个引用指向的同样的对象时equals()才会返回true。如果你不是很了解hashCode()和equals()的规则,可以看看这篇文章。
来看看HashMap最常用的方法,如迭代、打印等。
3. TreeMap
TreeMap的键按顺序排列。让我们先看个例子看看什么叫作“键按顺序排列”。
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
|
classDog {
String color;
Dog(String c) {
color = c;
}
publicbooleanequals(Object o) {
return((Dog) o).color ==this.color;
}
publicinthashCode() {
returncolor.length();
}
publicString toString(){
returncolor +" dog";
}
}
publicclassTestTreeMap {
publicstaticvoidmain(String[] args) {
Dog d1 =newDog("red");
Dog d2 =newDog("black");
Dog d3 =newDog("white");
Dog d4 =newDog("white");
TreeMap treeMap =newTreeMap();
treeMap.put(d1,10);
treeMap.put(d2,15);
treeMap.put(d3,5);
treeMap.put(d4,20);
for(Entry entry : treeMap.entrySet()) {
System.out.println(entry.getKey() +" - "+ entry.getValue());
}
}
}
|
输出:
1
2
3
|
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
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
|
classDogimplementsComparable<Dog>{
String color;
intsize;
Dog(String c,ints) {
color = c;
size = s;
}
publicString toString(){
returncolor +" dog";
}
@Override
publicintcompareTo(Dog o) {
return o.size -this.size;
}
}
publicclassTestTreeMap {
publicstaticvoidmain(String[] args) {
Dog d1 =newDog("red",30);
Dog d2 =newDog("black",20);
Dog d3 =newDog("white",10);
Dog d4 =newDog("white",10);
TreeMap treeMap =newTreeMap();
treeMap.put(d1,10);
treeMap.put(d2,15);
treeMap.put(d3,5);
treeMap.put(d4,20);
for(Entry entry : treeMap.entrySet()) {
System.out.println(entry.getKey() +" - "+ entry.getValue());
}
}
}
|
输出:
1
2
3
|
red dog - 10
black dog - 15
white dog - 20
|
结果根据键的排列顺序进行输出,在我们的例子中根据size排序的。
如果我们将“Dog d4 = new Dog(“white”, 10);”替换成“Dog d4 = new Dog(“white”, 40);”,那么输出会变成:
1
2
3
4
|
white dog - 20
red dog - 10
black dog - 15
white dog - 5
|
这是因为TreeMap使用compareTo()方法来比较键值的大小,size不相等的狗是不同的狗,如果颜色不同但是size相同则也算相同,具体看Dog类的cmpareTo()函数的实现。
4. Hashtable
Java文档写道:
HashMap类和Hashtable类几乎相同,不同之处在于HashMap是不同步的,也允许接受null键和null值。
5. LinkedHashMap
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
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
|
classDog {
String color;
Dog(String c) {
color = c;
}
publicbooleanequals(Object o) {
return((Dog) o).color ==this.color;
}
publicinthashCode() {
returncolor.length();
}
publicString toString(){
returncolor +" dog";
}
}
publicclassTestHashMap {
publicstaticvoidmain(String[] args) {
Dog d1 =newDog("red");
Dog d2 =newDog("black");
Dog d3 =newDog("white");
Dog d4 =newDog("white");
LinkedHashMap linkedHashMap =newLinkedHashMap();
linkedHashMap.put(d1,10);
linkedHashMap.put(d2,15);
linkedHashMap.put(d3,5);
linkedHashMap.put(d4,20);
for(Entry entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() +" - "+ entry.getValue());
}
}
}
|
输出:
1
2
3
|
red dog - 10
black dog - 15
white dog - 20
|
如果我们使用HashMap的话,输出将会如下,会打乱插入的顺序:
1
2
3
|
red dog - 10
white dog - 20
black dog - 15
|
我们今天的关于HashMap、HashTable、LinkedHashMap和TreeMap用法和区别和hashmap,linkedmap,treemap的区别的分享就到这里,谢谢您的阅读,如果想了解更多关于CurrentHashMap、HashMap、HashTable的区别、hashmap hashtable LinkedHashMap TreeMap、HashMap vs TreeMap vs Hashtable vs LinkedHashMap、HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa的相关信息,可以在本站进行搜索。
本文标签: