HashTable与HashMap

    hashtable与hashmap都实现了Map接口,

不同的是,hashtable同时继承了asbstract class Dictionary<K,V>

而hashmap继承了abstract class AbstractMap<K,V>,置于这两个抽象类的详细区别后文再说

还有一点就是hashmap是非线程安全的,hashtable是线程安全的。(其实就是后者在方法前面加了synchronized关键字)这个关键字使的该方法只能被串行执行。因此,hashtable的效率也就不如hashmap了。

下面首先看看hashtable一些方法的源码


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

        // overflow-conscious code
        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<?,?>[] 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;
            }
        }
    }

首先,上述方法时protected类型的。因此使用者无法显示调用此方法。

但是有个问题,这个方法没有加synchronized关键字修饰!那就非线程安全了?

别急,看看源码,源码内有个modCount属性,该属性的作用就是为了保证线程安全使用的

试想两个对象都在调用同一个hashtable对象,对其进行插入操作,但是对于这两个对象来说,modCount并非静态变量,因此肯定不相等,只要出现modCount不相等,系统马上就会报错(fail fast机制)因此,就保证了线程安全。

而没有采取synchronize关键字来修饰到达线程安全,是因为这个方法并不会被显示调用。

然后,分析一下源码运行步骤:

1:先对table进行扩容,扩容规则是(旧容量*2+1),注意当前的扩容并没有进行实质性的操作,只是把当前table的length拿出来而已。

2、接着对扩容后的新容量newLength进行判断,当newLength>数组最大长度,并且oldLength==数组最大长度时(这个数组最大长度是规定好的为:MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8),取消扩容操作,返回。

反之,当newLength>数组最大长度,并且oldLength!=数组最大长度时(此处其实默认oldLength<数组最大长度),开始进行的扩容操作。

3、中间的modCount、threshold等与扩容无关,是为了防止出错以及下一次扩容判断,此处不讲。

在进行扩容时,主要看for循环体,扩容需要把原来数组内的Entry放到新数组中,源码中是从后往前来遍历旧的数组。

并且对于index相同的Entry,是将新的e放在数组中,并将e.next指向原先在数组中的oldE,即将新的e放在链表的最前端。

老版本是将新的e放在链表的最末端

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;
        @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;
    }

上述方法时hashtable的put value方法,该方法主要体现了一点,value不能为null

这是hashtable与hashmap的另一个不同点

hashtable的entry继承的是hashtable$Entry.class

其中的value是这样定义的

public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

而hashmap中的entry继承的是hashmap$Node.class

其中value是这样定义的

public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
可以看的出来hashmap自由多了



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值