HashMap & HashTable

HashMap实现及冲突之谈 http://www.javaeye.com/topic/242468

开发地址散列:使用开放地址方法,数组的每个位置最多只有一项,因此不存在链表。当向表中插入某一项时,该项的键被分布到表的某个索引。如果该个索引所在位置是空的,该项就被插入到这个位置。否则,算法将系统地搜寻这个表的剩余部分,直到找到一个空的位置。
1)线性地址探测法:即将插入项的键分布到索引j上,如果table[j]为空,将这项插入该位置上。否则,检查索引j+1位置,然后检查j+2,依此类推,直至发现空槽。
缺点:所有分布到同一索引index上的键保持相同的路径:index,index+1,index+2,依此类推。这些非空位置形成了簇。每次向簇中添加一项,不仅簇变得更大,而且使之增长速度也加快。这样就形成主簇现象,影响检索性能。
主簇所指的就是冲突处理允许簇加速增长时出现的现象。

2)二次探查法(quadratic probing)   二次探查法的探查序列是:  hi=(h(key)+i*i)%m 0≤i≤m-1 //即di=i2   即探查序列为d=h(key),d+1^2,d+2^2,…,等。  该方法的缺陷是不易探查到整个散列空间。
3)双重散列法(double hashing)

该方法是开放定址法中最好的方法之一,它的探查序列是:  hi=(h(key)+i*h1(key))%m 0≤i≤m-1 //即di=i*h1(key)   即探查序列为:  d=h(key),(d+h1(key))%m,(d+2h1(key))%m,…,等。  该方法使用了两个散列函数h(key)和h1(key),故也称为双散列函数探查法。


如果不对所有键均使用相同的偏移,而是根据键计算偏移,可以避免主簇现象。

例:
int hash = key.hashCode();
int index = (hash & ox7FFFFFFF)%table.length;
如果有冲突:
int offset = hash/table.length; 即h1的实现方法。
int index = (index+offset)%table.length;

如果第一次移位还存在同样的冲突,则继续:当前冲突索引位置(索引号+余数)%表.length


-------------------------------------------------------------------------------

hashMap可以算作是hashtable的升级版本, 最早从1.2开始有的.
整体上hashMap对hashtable类优化了代码. 比如说, 消除了hardcoding, 增加了code reuse等等.

首先他们的相同点都实现了Map接口

但是, 两者之间最主要的不同有两点.
1.hashMap的读写是unsynchronized, 在多线程的环境中要注意使用
而hashtable是synchronized
这两者的不同是通过在读写方法上加synchronized关键字来实现的.

hashMap
public V put(K key, V value)
public V get(Object key)

hashtable
public synchronized V get(Object key)
public synchronized V put(K key, V value)

可能有人问, 能synchronized, 能线程安全好啊. 为什么不要呢?
这里其实还是一个效率的问题. 对于线程安全的方法, 系统要进行加锁, 减锁操作. 性能会有很大的影响. 由于很多程序是在单线程或者说是线程安全的情况下工作的, 所以用synchronized就显得多余了.

2.第二个不同是hashMap可以放空值, 而hashtable就会报错.
hashMap
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);

hashtable
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}



3. hashtable与hashmap在获得INDEX值的方法上的区别:都为取模,但实现方法不一样。

3.1"与操作"的效率要比"模操作"高出很多。

3.2 .(这里h的高位因为参与&运算后始终为0,从而可能会出现两个高位不同的hash值算出的数组下标却一样:这就是hashmap的hash方法里面对h进行一系列处理的原因),而这点在hashtable中没有实现。

hashtable

int hash = key.hashCode();0 V' A9 V# f3 X/ _: {
int index = (hash & 0x7FFFFFFF) % tab.length;

当tab.length为2的N次方时,取模运算最快,因为相当于简单的移位。

对key.hashCode()没有进行处理,直接使用其值;在计算数组下标时直接采用了普通的求模操作
hashmap

int h = key.hashCode();0 V' A9 V# f3 X/ _: {

static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }

int hash = hash(k); int i = indexFor(hash, table.length);

static int indexFor(int h, int length) { return h & (length-1); } 相当于对长度取模。可见页面2.



4.初始化数组大小不一样,HashMap是16,而Hashtable是11
public Hashtable() { this(11, 0.75f); }

HashMap的大小一定是2的幂数。如果初始化HashMap的时候指定了一个不是2的幂数的长度,它也会找到一个最接近你指定值的一个2的幂数.另外HashMap的长度是有最大值的

HashTalbe 它不会对你指定的值做额外处理;另外,没有最大仁值的限制.(因为是取模操作,所以可以不是2的幂数。但是最好是,这样,取模就简化成移位,有利于速度的加快)



5. 扩容机制也不同,HashMap是旧的大小×2,而Hashtable是旧的大小×2+1

Hashtable
protected void rehash() { int oldCapacity = table.length; Entry[] oldMap = table; int newCapacity = oldCapacity * 2 + 1; Entry[] newMap = new Entry[newCapacity]; modCount++; threshold = (int)(newCapacity * loadFactor); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newMap[index]; newMap[index] = e; } }


------------------------------------------------------

HashMap类源代码欣赏 http://liuxinglanyue.javaeye.com/blog/811167

------------------------------------------------------

JVM里面hashtable实现原理

http://tech.techweb.com.cn/thread-461939-1-1.html ---------1

-----------------------------------------------------

JVM里面hashmap实现原理

http://grunt1223.javaeye.com/blog/544497 --------2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值