Java中的Hashtable实现方法

首先,先上Hashtable.class中的代码,所有的Java实现方法都在这个文件中了。

当然,对于这个文件的内容,你现在无需看懂它,如果你看懂了,我们下面也就没有意义讲解了。
现在,来让我们回顾下,在Java中使用Hashtable中的方法:

  Hashtable使用的方法
import java.util.Hashtable;
 
public class test {
 
    public void useHashtable()
    {
        String key = "key";
        String value = "value";
        Hashtable ht = new Hashtable<String,String>();
        ht.put(key, value);
        ht.remove(key);
    }
}

接下来,让我们从上面的使用方法说起。
对于,构造Hashtable的那一行,我们看看对应到Hashtable.class文件中的调用,如下:

  Hashtable初始化
/**
 * Constructs a new, empty hashtable with a default initial capacity (11)
 * and load factor (0.75).
 */
public Hashtable() {
this(11, 0.75f);
}
 
/**
 * Constructs a new, empty hashtable with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hashtable.
 * @param      loadFactor        the load factor of the hashtable.
 * @exception  IllegalArgumentException  if the initial capacity is less
 *             than zero, or if the load factor is nonpositive.
 */
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal Load: "+loadFactor);
 
    if (initialCapacity==0)
        initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);
}

这里注意,当你使用无参的构造函数时,默认传入的initialCapacity=11,loadFactor=0.75.
Ok,你一定想问initialCapacity和loadFactor都是什么?我们看到构造函数中有这么两句语句:

table = new Entry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);

这里的table变量时一个Entry数组,Entry你可以认为是一条记录,所以从第一句,我们就可以知道Hashtable就是一个Entry的数组。那么既然是数组,为什么还要提出Hashtable的概念?这个问题先放放,我们先来看loadFactor,看threshold的那句,你就能明白,其实loadFactor是用来判断是否要扩建的。所以,我们可以认为这里,当Hashtable的75%被使用时,就扩建。
现在让我们来解答,这个Entry数组为什么是Hashtable,而不仅仅是数组。让我们来看看Entry的定义:

  Entry的类定义
/**
     * Hashtable collision list.
     */
    private static class Entry<K,V> implements Map.Entry<K,V> {
    int hash;
    K key;
    V value;
    Entry<K,V> next;
 
    protected Entry(int hash, K key, V value, Entry<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
 
    protected Object clone() {
        return new Entry<K,V>(hash, key, value,
                  (next==null ? null : (Entry<K,V>) next.clone()));
    }
 
    // Map.Entry Ops
 
    public K getKey() {
        return key;
    }
 
    public V getValue() {
        return value;
    }
 
    public V setValue(V value) {
        if (value == null)
        throw new NullPointerException();
 
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }
 
    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
        return false;
        Map.Entry e = (Map.Entry)o;
 
        return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
           (value==null ? e.getValue()==null : value.equals(e.getValue()));
    }
 
    public int hashCode() {
        return hash ^ (value==null ? 0 : value.hashCode());
    }
 
    public String toString() {
        return key.toString()+"="+value.toString();
    }
    }

这里,我们关注它的四个字段,hash、key、value、next。这里的hash也就是之所以叫Hashtable的原因之一。这个hash数值是根据特定的hash算法得出的,详细内容可以找相应的hash算法资料,这里就不介绍了。接下来说,next字段,next字段是当hash产生重复的时候,以链表保存新传入的hash值对应的key和value。看图说话,就应该很明白了。
Hashtable结构图
上面的图应该就已经很清楚了。列表和数组共存的方法。
这里有个题外话,就是前段时间,有个关于post大量相同hash的数据,使得hashtable大量退化成链表。导致查询速度奇慢的问题。也就是因为这里的这个机制。具体,怎么会产生hash碰撞,这和语言本身采用的hash算法有关。
看完了初始化,我们接下来看下Hashtable的put方法。代码如下:

  put方法的函数体
/**
     * Maps the specified <code>key</code> to the specified
     * <code>value</code> in this hashtable. Neither the key nor the
     * value can be <code>null</code>. <p>
     *
     * The value can be retrieved by calling the <code>get</code> method
     * with a key that is equal to the original key.
     *
     * @param      key     the hashtable key
     * @param      value   the value
     * @return     the previous value of the specified key in this hashtable,
     *             or <code>null</code> if it did not have one
     * @exception  NullPointerException  if the key or value is
     *               <code>null</code>
     * @see     Object#equals(Object)
     * @see     #get(Object)
     */
    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;
    }

其他地方都没有什么好说的,关键看for语句这里,这里就是查询到对应的Entry后,再将对应的value赋给链表中正确的对应项。
紧接着的下面的if语句,就是扩建机制,也没有什么可以多说的。最后一段内容,就是如果原有的Hashtable中没有对应项,就等于新增。
有了put的基础,我们再来看看remove的情况,你应该很容易就能看懂。

  remove方法的函数体
/**
 * Removes the key (and its corresponding value) from this
 * hashtable. This method does nothing if the key is not in the hashtable.
 *
 * @param   key   the key that needs to be removed
 * @return  the value to which the key had been mapped in this hashtable,
 *          or <code>null</code> if the key did not have a mapping
 * @throws  NullPointerException  if the key is <code>null</code>
 */
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
    modCount++;
    if (prev != null) {
        prev.next = e.next;
    } else {
        tab[index] = e.next;
    }
    count--;
    V oldValue = e.value;
    e.value = null;
    return oldValue;
    }
}
return null;
}

同样,这个也没有可以多说的地方,仍然是for语句。逻辑和put的差不多。就是找到对应的项,然后做操作。

基本上整个Hashtable的实现也就如此了。

posted on 2013-04-24 14:56  duanxz  阅读(630)  评论(0编辑  收藏  举报