HashMap和HashTable的区别和不同

两者内部实现都是"数组+链表"的结构,可以从以下几方面理解两者的不同(参考源代码):
1. 数组大小
[b]HashMap的默认大小:[/b]
static final int DEFAULT_INITIAL_CAPACITY = 16;
HashMap的大小一定是2的幂数。如果初始化HashMap的时候指定了一个不是2的幂数的长度,它也会找到一个最接近你指定值的一个2的幂数,源码:
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;

这么做的原因将在下面详细说明.另外HashMap的长度是有最大值的:
static final int MAXIMUM_CAPACITY = 1 << 30;
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;


[b]HashMap的默认大小:[/b]
public Hashtable() {
this(11, 0.75f);
}

默认为11,如果你指定了它的初始长度,它不会对你指定的值做额外处理;另外,没有最大仁值的限制

2. 数组扩容
在实际中真正利用的长度并不是初始大小,而是有个加载因子,默认是0.75,比如长度是16,而真正使用的是16*0.75,当超过这个数,就会扩容
[b]HashMap扩容:[/b]
HashMap扩容会把之前长度*2,因为之前的长度肯定是2的幂数,所以自动扩容后也是2的幂数

[b]HashTable扩容:[/b]
HashTable扩容是把之前长度*2+1
扩容操作是比较消耗资源的,所以这里我们告诉我们在初始化HashMap和HashTable的时候要考虑到实际使用时的长度,应该尽可能的避免出现扩容的操作,从而提高效率

3. put操作
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}

从putForNullKey可以看出:HashMap是允许键值为null的(至多存在一个);
接下来看hash方法:
private static int hash(int h) {
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}

这里的一系列移位操作是为了让h的高位参与接下来的运算,具体为什么是4,9,10,14这几个数,还不是很清楚.
接下来计算数组下标:
static int indexFor(int h, int length) {
return h & (length-1);
}

这里的h&(length-1)是HashMap实现最出彩的地方,也是HashMap为什么比HashTable的效率要高的原因:
因为length一定是2的幂数,故length-1是一个位数少1位,而且全部为1的二进制数,将它与h执行&操作,其值域等效于h%(length-1)的值域.而我们知道"与操作"的效率要比"模操作"高出很多.(这里h的高位因为参与&运算后始终为0,从而可能会出现两个高位不同的hash值算出的数组下标却一样:这就是上面的hash方法里面对h进行一系列处理的原因)

[b]HashTable的put:[/b]
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}

// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}

modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}

// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);
count++;
return null;
}

可以看出HashTable是同步的,而且不允许空值(这两点和HashMap是不同的).
对key.hashCode()没有进行处理,直接使用其值;在计算数组下标时直接采用了普通的求模操作(hash & 0x7FFFFFFF执行了取绝对值操作,保证计算出的下标为正值)


[b]总结: [/b]
1. HashMap是没有同步操作的,而HashTable是有的
2. HashMap的效率要比HashTable的效率高(求数组下标这一点最明显)
3. HashMap允许key值为null,而HashTable是不允许的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值