HashTable源码

一直写一些工作日常笔记,最近想静下来看下一些常见的知识原理,在很多blog上都有看到过,不过看到的东西是别人的,只有自己去看了源码,写了demo,理解了才是自己的东西。

hashTable(jdk1.7)

定义:
继承与Dictionary,实现了Map的一些方法,标记了这个对象Clone,以及序列化Serializable

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

构造器及初始化:
不传任何参数的构造器,系统默认容量为11,hash因子0.75

new Hashtable();//this(11, 0.75f);

传入容量大小的initialCapacity,

Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

传入容量(initialCapacity)和hash因子(loadFactor)

 new Hashtable(int initialCapacity, float loadFactor) 
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);
        //容量为0,设置一个容量为,保证传入不是IsEmpty
        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        //创建一个Entry数组,Entry是一个单链表结构
        table = new Entry[initialCapacity];
        //设置一个阀值threshold=容量*hash因子,超过这个阀值,需要对数组扩容
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        initHashSeedAsNeeded(initialCapacity);
}

构造其中主要是初始化容量值,hash因子,threshold,创建一个单链表对象数组。还看到一个initHashSeedAsNeeded方法,这个方法主要初始化hashSeed参数,其中hashSeed用于计算key的hash值,它与key的hashCode进行按位异或运算。这个hashSeed是一个与实例相关的随机值,主要用于解决hash冲突。

重要方法:

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;
        //使用hashSend与运算key.hashCode
        int hash = hash(key);
        //hash & 0x7FFFFFFF防止hash为负值吧 
        //这里使用除法散列
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        //遇到hash值相同的插入,使用单链表连接起来,解决hash冲突
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }
       //modCount没修改一次计数加1,与添加数量无关,用于遍历时候检查操作合法性
        modCount++;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            //重hash 数组中元素超过阀值,需要再次计算容量,扩容数组
            rehash();

            tab = table;
            hash = hash(key);
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.每次扩容创建新数组,开销比较大,所以给定好初始容量比较重要,减少数组扩容
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }

put方法比较简单,就是先对key值hash然后与hashSend这个随机值与运行,这样做的好处是减少hash重复。然后使用除法散列法,把对象放入到entry数组,如果遇到hash值重复,使用单链表把相同hash值的元素连接起来。但数组达到阀值(threshold),对创建一个新数组进行扩容。
接下来看下rehash

protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;

        // overflow-conscious code
        //计算新数组容量,左移1位,new=2*old+1
        int newCapacity = (oldCapacity << 1) + 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<K,V>[] newMap = new Entry[newCapacity];

        modCount++;
        //重新生成阀值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        boolean rehash = initHashSeedAsNeeded(newCapacity);

        table = newMap;
        //将原来的元素拷贝到新的HashTable中  
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                if (rehash) {
                    e.hash = hash(e.key);
                }
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = newMap[index];
                newMap[index] = e;
            }
        }
    }

接下看看putAll,这个方法没什么说的,遍历传入集合,最终还是调用put方法

    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

get方法 先hash转入key,然后找到对应的index,在entry数组中找到对应的entry对象,再通过hash值和key比较找到想要的对象值返回

    public synchronized V get(Object key) {
        Entry tab[] = table;
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            //hash重复,判断key是否同一个对象
            if ((e.hash == hash) && e.key.equals(key)) {
                return e.value;
            }
        }
        return null;
    }

看完获取,添加元素后,再来看看删除
同样先hash转入key,然后找到对应的index,在entry数组中找到对应的entry对象,再通过hash值和key比较找到想要的对象,如果当前entry没有prev,直接把该对象的next指向tab[index],断开该对象的链表;
这里写图片描述
如果该对象有prev,和next就把该对象的prev.next指向该对象的next

这里写图片描述

    public synchronized V remove(Object key) {
        Entry tab[] = table;
        int hash = hash(key);
        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;
    }

clear没什么可说的 把所有的元素置null

   public synchronized void clear() {
        Entry tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

迭代器Enumeration:
hashTable使用的是Enumeration迭代器接口
 Enumeration接口是JDK1.0时推出的,最早使用Vector时就是使用Enumeration接口进行输出。虽然Enumeration是一个旧的类,但是在JDK1.5之后为Enumeration类进行了扩充,增加了泛型的操作应用。

 Enumeration接口常用的方法有hasMoreElements()(判断是否有下一个值)和 nextElement()(取出当前元素),这些方法的功能跟Iterator类似,只是Iterator中存在删除数据的方法,而此接口不存在删除操作。

HashTable与HashMap的区别

第一:我们从他们的定义就可以看出他们的不同,HashTable基于Dictionary类,而HashMap是基于AbstractMap。
(Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。)

第二:HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。
第三:迭代器不同,hashTable使用的比较古老的Enumeration,而hashMap使用的是Interator
第四:Hashtable的方法是同步的,而HashMap的方法不是。所以有人一般都建议如果是涉及到多线程同步时采用HashTable,没有涉及就采用HashMap,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值