7.WeakHashMap VS HashMap VS Hashtable
WeakHashMap与HashMap实现大体一致,但WeakHashMap对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。HashMap与Hashtable实现大体一致,但Hashtable支持线程同步,HashMap不支持。需要同步操作的理由是,可能存在多个线程对同一个集合进行操作的情况:譬如一个线程正在对某集合进行遍历,但与此同时,另一个线程又在对该集合进行插入或删除,那么第一个线程的遍历结果将是不可预测的,对于同步集合,它将会抛出一个ConcurrentModificationException异常,JCF把这种机制成为“fail-fast”。
8.使用HashMap与 WeakHashMap需要注意的问题
public class Testk {
int i = 1;
public Testk(int i) {
this.i = i;
}
public int getI() {
return i;
}
public boolean equals(Object test){
if((test instanceof Testk) && i == ((Testk)test).i){
return true;
}else return false;
}
}
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.HashMap;
import java.util.Map.Entry;
public class MapTry {
HashMap<Testk, Integer> weak = new HashMap<Testk, Integer>();
Testk test1 = new Testk(1);
Testk test2 = new Testk(2);
Testk test3 = new Testk(3);
Testk test4 = new Testk(4);
Testk test5 = new Testk(5);
Testk test6 = new Testk(6);
public MapTry() {
weak.put(test1,1);
weak.put(test2,2);
weak.put(test3,3);
weak.put(test4,4);
weak.put(test5,5);
weak.put(test6,6);
}
public static void main(String[] args){
MapTry k = new MapTry();
Testk test7 = new Testk(5);
System.out.println(k.weak.get(test7));
}
}
输出的结果是null,为什么输出不是5?
test7.equals(test5)==true,怎么k.weak.get(test7)会得到null呢?
原因是HashMap存储,查找,删除对象时,先算出对象的HashCode值,在经过一个“一定”的算法,得到一个整形值h,再将对象存于数组元素Entry<K,V> table[h]中。
static int hash(Object x) {
int h = x.hashCode(); //h是x的HashCode
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
而HashMap的HashCode方法继承自Object,HashCode方法返回的HashCode对应于当前的地址,对于不同的对象,即使它们的内容完全相同,用HashCode()返回的值也不同。
这就要求我们覆盖HashCode方法(有很多书介绍如何写出合理的HashCode方法)。把Testk改成:
public class Testk {
int i = 1;
public Testk(int i) {
this.i = i;
}
public int getI() {
return i;
}
public boolean equals(Object test){
if((test instanceof Testk) && i == ((Testk)test).i){
return true;
}else return false;
}
public int hashCode(){
return i;
}
}
另外还要注意的一点是,上面的equals方法不能写成:
public boolean equals(Testk test){
if(i == test.i){
return true;
}else return false;
}
这是因为java.util.HashMap中对比两个对象是否相等用的是equals(Object o)方法。
9.TreeMap VS HashMap
TreeMap按照树形组织元素,但是以Iterator输出时是按照固定顺序的,这种顺序按照key的比较,从小到大。HashMap没有固定的顺序,但是不管从添加,查询,删除的执行效率都要比TreeMap高。
10.IdentityHashMap VS HashMap
IdentityHashMap对键成员采用的是严格相等比较,下面是IdentityHashMap的取值方法:
public V get(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k) //对比Key
return (V) tab[i + 1];
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}
对比Key是否相等用的是item == k,而不是item == k || item.equals(k),所以两个键对象是同一内存对象时才相等,即由同一个new操作产生才相等。还有一点就是HashMap的存储结构与IdentityHashMap的存储结构不一样,HashMap的Entry<K,V> table[]的数组元素都相当于一个桶,HashCode相同的实体(Entry<K,V>)存在同一个table[i]下,如table[2]下可能存储几个Entry<K,V>,每个Entry<K,V>中有一个私有变量(Entry<K,V> next),该变量指向下一个Entry<K,V>,这样几个Entry<K,V>就以链式结构存储于table[2]中。但IdentityHashMap中如果HashCode值相同,就会通过一个固定的算法重复运算,直到得到的HashCode值对应的table[i]中未被存储过,才将其存进去。
11.HashMap VS LinkedHashMap
HashMap与LinkedHashMap存储结构一样,不过用Iterator遍历HashMap时得到的元素是没有先后顺序的,而LinkedHashMap把加入的元素按加入先后顺序连起来,用Iterator遍历时按加入的先后得到各个元素。需要注意的一点是:
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
如果accessOrder为true时,用get(Object key)得到元素后,该实体会从链中取出,然后连接到链的链尾,默认accessOrder为false。
12.EnumMap
枚举类型定义一个固定的、封闭的值集合,然后,在需要这些值中的某一个值时,可以通过它的名称来指定它,这就是枚举类型的简单性.枚举类型的值就是枚举类型的实例,编译器会确保没有传入其他的类型,这就是枚举类型的安全性.但我感觉EnumMap用的比较少,可以用其它集合结构取代。当然,既然它存在于java.util.*中,就必然有其优势。