美文网首页
jdk源码分析之HashTable

jdk源码分析之HashTable

作者: shoulda | 来源:发表于2018-06-25 21:42 被阅读0次

1.定义

HashTable在java中的定义如下:

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

可以看出HashTable继承Dictionary类,实现Map接口。其中Dictionary类是可以将任何键映射到值的类的抽象父类。每个键和每个值都是一个对象。

HashTable定义了几个重要的参数:table,count,threhold,loadFactor

table:为一个Entry[]数组类型,Entry代表了节点,每一个Entry代表一个键值对。
count:HashTable的大小,指的是他包含Entry键值对的数量。
threshold:HashTable的阈值
loadFactor:加载因子

2.构造方法

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

默认构造函数,容量为11,加载因子为0.75

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,获得大小为initialCapacity的table数组
        table = new Entry[initialCapacity];
        //计算阀值
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        //初始化HashSeed值
        initHashSeedAsNeeded(initialCapacity);
    }

指定初始容量和指定加载因子,initHashSeedAsNeeded方法用于初始化hashSeed参数,其用于计算key的hash值,它与key的hashCode进行按位异或运算,hashSeed随机值主要是为了解决hash冲突。

3.主要方法

public synchronized V put(K key, V value) {
        // 确保value不为null
        if (value == null) {
            throw new NullPointerException();
        }

        /*
         * 确保key在table[]是不重复的
         * 处理过程:
         * 1、计算key的hash值,确认在table[]中的索引位置
         * 2、迭代index索引位置,如果该位置处的链表中存在一个一样的key,则替换其value,返回旧值
         */
        Entry tab[] = table;
        int hash = hash(key);    //计算key的hash值
        int index = (hash & 0x7FFFFFFF) % tab.length;     //确认该key的索引位置
        //迭代,寻找该key,替换
        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();
            tab = table;
            hash = hash(key);
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // 在索引位置处插入一个新的节点
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        //容器中元素+1
        count++;
        return null;
    }

由上可知,根据key的hash值,根据hash值获得key在table数组中的索引位置,然后迭代该key处的Entry链表,如果该链表存在一个这样的key对象,那么直接替换value值即可,否则就讲该key-value节点插入到节点处。

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) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return e.value;
            }
        }
        return null;
    }

get方法比较简单,计算key的hash值,判断在table数组中的索引位置。

HashTable的put方法中的扩容操作

HashTable中的扩容操作

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

        //新容量=旧容量 * 2 + 1
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }

        //新建一个size = newCapacity 的HashTable
        Entry<K,V>[] newMap = new Entry[];

        modCount++;
        //重新计算阀值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        //重新计算hashSeed
        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;
            }
        }
    }

rehash()是将原来的容量扩大两倍 + 1,同时需要将原来的HashTable中的元素复制到新的HashTable中。

4.HashTable和HashMap的区别

1.HashTable是基于Dictionary类,而HashMap是基于AbstractMap.
2.HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable的key和value都不能为null.
3.HashTable的方法是同步的,而HashMap不是,所以一般涉及多线程都推荐用HashTable。

相关文章

  • jdk源码分析之HashTable

    1.定义 HashTable在java中的定义如下: 可以看出HashTable继承Dictionary类,实现M...

  • Java 集合类原理

    Java基础——HashMap源码分析 Java基础——HashSet源码分析 Java基础——HashTable...

  • 00_Hashtable学习

    Hashtable源码分析 基于Java8Hashtable.class的说明: This class imple...

  • ConcurrentHashMap 原理和源码分析(一)

    通过之前几篇文章《HashMap原理和源码分析》 《HashTable原理和源码分析》《LinkedHashMap...

  • Java基础之LinkedHashMap源码解析

    Java集合源码解析系列 Java基础之HashMap源码解析 Java基础之HashTable源码解析 Java...

  • Hashtable源码分析

    集合特性 对于集合框架我们的关注点一般在一下几点: 集合底层实现的数据结构是什么 数组+链表 集合中元素是否允许...

  • Hashtable源码分析

    Hashtable Hashtable简介 和HashMap一样,Hashtable也是一个散列表,它存储的内容是...

  • Hashtable源码分析

    以下内容整理自互联网,仅用于个人学习 Hashtable简介 HashTable同样是基于哈希表实现的,同样每个元...

  • HashTable源码分析

    一、前言 前面几篇介绍了List相关的几个类。本篇开始分析Map相关的集合常用类的源码,OK,从HashTable...

  • HashTable源码分析

    HashTable跟HashMap在功能上来基本类似,其解决hash冲突的方法也是基于链地址法, 唯一的不同点在H...

网友评论

      本文标题:jdk源码分析之HashTable

      本文链接:https://www.haomeiwen.com/subject/htluyftx.html