1. 基本使用
a. 在HashMap中通过get()来获取value,通过put()来插入value,ContainsKey()则用来检验对象是否已经存在。可以看出,和ArrayList的操作相比,HashMap除了通过key索引其内容之外,别的方面差异并不大;
b. 可以使用values()方法返回一个Collection物件,如果您需要一次选代Map中所有的物件;
c. HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序;
d. HashMap使用Hash Table,因而它有自己的排序方式,如果您想要在选代所有的物件时,依照插入的顺序来排序,则可以使用LinkedHashMap,它是HashMap 的子类。
System.out.println("Sequence...");
Map hashmap = new HashMap();
hashmap.put("a", "aa");
hashmap.put("b", "bb");
hashmap.put("c", "cc");
hashmap.put("d", "dd");
Iterator iterator_hashmap = hashmap.keySet().iterator();
while (iterator_hashmap.hasNext()) {
Object key = iterator_hashmap.next();
System.out.println("HashMap:" + hashmap.get(key));
}
Map linkedhashmap = new LinkedHashMap();
linkedhashmap.put("a", "aa");
linkedhashmap.put("b", "bb");
linkedhashmap.put("c", "cc");
linkedhashmap.put("d", "dd");
Collection collection = linkedhashmap.values();
Iterator iterator_linkedhashmap = collection.iterator();
while (iterator_linkedhashmap.hasNext()) {
System.out.println("LinkedHashMap:" + iterator_linkedhashmap.next());
}
Hashtable hashtable = new Hashtable();
hashtable.put("a", "aa");
hashtable.put("b", "bb");
hashtable.put("c", "cc");
hashtable.put("d", "dd");
Iterator iterator_hashtable = hashtable.keySet().iterator();
while (iterator_hashtable.hasNext()) {
Object key = iterator_hashtable.next();
System.out.println("HashTable:" + hashtable.get(key));
}
Map treemap = new TreeMap();
treemap.put("a", "aa");
treemap.put("b", "bb");
treemap.put("c", "cc");
treemap.put("d", "dd");
Iterator iterator_treemap = treemap.keySet().iterator();
while (iterator_treemap.hasNext()) {
Object key = iterator_treemap.next();
System.out.println("TreeMap:" + treemap.get(key));
}
2. keySet 和 entrySet 的比较
对于keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以就快了。 HashMap使用很多,比如导入信息时就要用到,因大部分导入的信息要去判断是否有重复的信息,这样就可以利用containsKey来进行处理了,而不用在插入的时候去进行处理。
System.out.println("efficiency of keySet and entrySet...");
HashMap k_hashmap = new HashMap();
for (int i = 0; i < 1000; i++) {
k_hashmap.put("" + i, "hello");
}
long k_bs = Calendar.getInstance().getTimeInMillis();
Iterator iterator = k_hashmap.keySet().iterator();
while (iterator.hasNext()) {
System.out.println(k_hashmap.get(iterator.next()));
}
System.out.println(Calendar.getInstance().getTimeInMillis() - k_bs);
HashMap e_hashmap = new java.util.HashMap();
for (int i = 0; i < 1000; i++) {
e_hashmap.put("" + i, "hello");
}
long e_bs = Calendar.getInstance().getTimeInMillis();
java.util.Iterator it = e_hashmap.entrySet().iterator();
while (it.hasNext()) {
java.util.Map.Entry entry = (java.util.Map.Entry) it.next();
System.out.println(entry.getValue());
}
System.out.println(Calendar.getInstance().getTimeInMillis() - e_bs);
3. HashCode在其中的探讨
下面的程序无论运行多少次,得到的结果都是"Not found"。也就是说索引KeyElement(5)并不在HashMap中.
KeyElement的HashCode方法继承自Object,而Object中的HashCode方法返回的HashCode对应于当前的地址,也就是说对于不同的对象,即使它们的内容完全相同,用HashCode()返回的值也会不同。这样实际上违背了我们的意图。因为我们在使用HashMap时,希望利用相同内容的对象索引得到相同的目标对象,这就需要HashCode()在此时能够返回相同的值。在下面的例子中,我们期望new KeyElement(i) (i=5)与 KeyElement test=new KeyElement(5)是相同的,而实际上这是两个不同的对象,尽管它们的内容相同,但它们在内存中的地址不同。因此很自然的,下面的程序得不到我们设想的结果。
public class KeyElement {
int number;
public KeyElement(int n) {
number = n;
}
}
HashMap hm = new HashMap();
for(int i=0; i<10; i++)
hm.put(new KeyElement(i), "aa"+1);
KeyElement testKey = new KeyElement(5);
if (hm.containsKey(testKey)) {
System.out.println(hm.get(testKey));
} else {
System.out.println("Not found");
}
如果我们把KeyElement类重新修改一下,就可以得到我们要的结果了。在这里KeyElement覆盖了Object中的hashCode()和equals()方法。覆盖hashCode()使其以number的值作为hashcode返回,这样对于相同内容的对象来说它们的hashcode也就相同了。而覆盖equals()是为了在HashMap判断两个key是否相等时使结果有意义
public class KeyElement {
int number;
public KeyElement(int n) {
number = n;
}
public int hashCode() {
return number;
}
public boolean equals(Object o) {
return (o instanceof KeyElement) && (number == ((KeyElement) o).number);
}
}
延伸:
重写HashCode()的原则:
- 不必对每个不同的对象都产生一个唯一的hashcode,只要你的HashCode方法使get()能够得到put()放进去的内容就可以了。即"不为一原则"。
- 生成hashcode的算法尽量使hashcode的值分散一些,不要很多hashcode都集中在一个范围内,这样有利于提高HashMap的性能。即"分散原则"。
掌握了这两条原则,你就能够用好HashMap编写自己的程序了。不知道大家注意没有,java.lang.Object中提供的三个方法:clone(),equals()和hashCode()虽然很典型,但在很多情况下都不能够适用,它们只是简单的由对象的地址得出结果。这就需要我们在自己的程序中重写它们,其实java类库中也重写了千千万万个这样的方法。利用面向对象的多态性——覆盖,Java的设计者很优雅的构建了Java的结构,也更加体现了Java是一门纯OOP语言的特性。
4. HashMap与HashTable的区别
a.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。
b.HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。
c.HashTable有一个contains(Object value),功能和containsValue(Object value)功能一样。
d.HashTable使用Enumeration,HashMap使用Iterator。
以上只是表面的不同,它们的实现也有很大的不同。
e.HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
f.哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
int hash = hash(k);
int i = indexFor(hash, table.length);
static int hash(Object x) {
int h = x.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
static int indexFor(int h, int length) {
return h & (length-1);
}
HashMap对null的操作