java-HashTable

目录

 

基本概念

基本属性

构造函数

无参构造

有初始化长度构造

有初始化长度以及负载因子构造

常用方法

put(K key, V value)

addEntry

remove

get

总结


基本概念

底层是Map

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable 

基本属性

  /**  数据存储的数组
     * The hash table data.
     */
    private transient Entry<?,?>[] table;

    /**容器中元素个数
     * The total number of entries in the hash table.
     */
    private transient int count;

    /**容器扩容阀值
     * The table is rehashed when its size exceeds this threshold.  (The
     * value of this field is (int)(capacity * loadFactor).)
     *
     * @serial
     */
    private int threshold;

    /**容器扩容的负载因子
     * The load factor for the hashtable.
     *
     * @serial
     */
    private float loadFactor;

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数

无参构造

默认数组长度为11,负载因子0.75

public Hashtable() {
        this(11, 0.75f);
    }

有初始化长度构造

public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f); // 默认0.75
    }

有初始化长度以及负载因子构造

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)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

常用方法

put(K key, V value)

public synchronized V put(K key, V value) {
        // HashTable不允许value为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;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }
  1. value不能为null
  2. 遍历map 如果该位置有值  则返回  否则调用addEntry方法

addEntry

private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        if (count >= threshold) {// 如果达到扩容条件,则进行扩容。
            // Rehash the table if the threshold is exceeded
            rehash();
            // 完成扩容后,需要重新计算本次要插入的key的哈希值和在table中的槽位
            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // 创建新的Entry节点,并插入到table中
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }



// 扩容
protected void rehash() {
        // 先保留老的数据暂时留存备用
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // 计算新的扩容数组大小,左移计算可以理解为原来的数值乘以2
        int newCapacity = (oldCapacity << 1) + 1;  // new = old*2 +1

        //判断扩容是否扩容的过大了,超过允许范围了。
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        // 声明一个新的数组
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        // 重新计算新数组的扩容阀值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;
        
        // 遍历最开始留存的老的数组中的所有元素,对每个元素重新计算哈希值和在新数组中的槽位,一个不落的插入到新数组中
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

remove

public synchronized V remove(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        // 遍历链表中的数据,e表示当前节点,pre记录的是上一个节点
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            // 匹配到需要删除的节点
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                if (prev != null) { // pre不为空,直接上一个节点的next跳过当前节点就是删除当前节点了。
                    prev.next = e.next;
                } else { // pre为空,说明要删除的是头结点
                    tab[index] = e.next;
                }
                count--;
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

get

public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

总结

方法前都加了修饰符synchronized,所以是线程安全的,由于是悲观锁,性能不高

上一篇:PAT B1033 旧键盘上的几个键又毁坏了,于是在输入一段文字时,对应得的字符就不会出现。


下一篇:Hash表的用处