Java中的Hashtable实现方法

来源:互联网 发布:配眼镜 知乎 编辑:程序博客网 时间:2024/06/05 14:35

现在,来让我们回顾下,在Java中使用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。看图说话,就应该很明白了。



上面的图应该就已经很清楚了。列表和数组共存的方法。
这里有个题外话,就是前段时间,有个关于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的实现也就如此了。





0 0
原创粉丝点击