首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >周末晚上回来写的HashTable源码分析

周末晚上回来写的HashTable源码分析

作者头像
码农王同学
发布2020-12-25 14:13:07
发布2020-12-25 14:13:07
4050
举报
文章被收录于专栏:后端Coder后端Coder

一,HashTable源码分析

源码分析三部曲,构造函数,方法分析,总结一下

二,方法分析

2.1,构造函数

代码语言:javascript
复制

//构造一个初始值为11,负载因子为0.75 
    public Hashtable() {
        this(11, 0.75f);
    }
//我们看下具体的操作实现
 public Hashtable(int initialCapacity, float loadFactor) {
     //如果初始容量小于0,则应该抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
     //负载因子也是不可能要小于等于0的,思考一下为什么?
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
     //构建初始容量为initialCapacity大小的entry
        table = new Entry<?,?>[initialCapacity];
     //阈值的计算,这个后面我自己单独看下为啥要这么计算
     //此时,我真的不知道为啥这么设置
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

为什么初始容量小于0抛出异常?因为数组的特点,数组下标是从0开始的。

2.2,put()方法

代码语言:javascript
复制
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;
       //拿到key的hashCode值
        int hash = key.hashCode();
       //计算key应该插入的位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
       //下面的逻辑就是判断key是否已经存在了
       //如果key已经存在,则直接替换,如果不存在,则执行下面的addEntry方法
       //首先根据index获取对应的entry数据
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            //如果hash值相等且key值相等,则说明已经存在了
            if ((entry.hash == hash) && entry.key.equals(key)) {
                //获取旧值
                V old = entry.value;
                //将新值赋值给entry.value
                entry.value = value;
                //返回旧值即可
                return old;
            }
        }
       //将新元素插入到对应的位置
        addEntry(hash, key, value, index);
        return null;
    }

//第二步
 private void addEntry(int hash, K key, V value, int index) {
        modCount++;
       //获取table引用赋值给临时变量tab[]
        Entry<?,?> tab[] = table;
       //rehash()操作也相当于集合中的数组扩容操作了
        if (count >= threshold) {
            //rehash操作这里暂时不分析了,因为文章太长了
            rehash();

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

        
        @SuppressWarnings("unchecked")
        //获取index位置的元素entry,然后构造一个entry放入table[index]位置
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
     // entry的数量加一
        count++;
    }

2.3,get()方法

代码语言:javascript
复制
public synchronized V get(Object key) {
     //将table引用赋值给临时局部变量tab[]
        Entry<?,?> tab[] = table;
     //获取key的hash值,其实就是为了确定index做准备的
        int hash = key.hashCode();
     //hash取模操作
        int index = (hash & 0x7FFFFFFF) % tab.length;
     //遍历table[]中的每一个元素entry,,比对hash值和key是否相等,
     //若相等则返回entry对应的元素,否则返回null,此时循环查找元素的时间复杂度O(n)
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

2.4,contains()方法

代码语言:javascript
复制
public synchronized boolean contains(Object value) {
    //判断元素value是否在hash表里面
    //由于put的时候,不会存在value为null的元素
    //这里相当于做个前置校验操作了
        if (value == null) {
            throw new NullPointerException();
        }
  //成员变量table赋值给临时局部变量,也是编程中常用的一种写法了
        Entry<?,?> tab[] = table;
    //为什么要从后往前找?
        for (int i = tab.length ; i-- > 0 ;) {
            //找到则返回true,否则返回false
            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }

2.5,containsValue()方法

代码语言:javascript
复制
public boolean containsValue(Object value) {
    //方法的复用,便于代码的扩展,可读性
        return contains(value);
    }

2.6,containsKey()方法

代码语言:javascript
复制
public synchronized boolean containsKey(Object key) {
    //将table成员变量的引用赋值给临时局部变量tab
        Entry<?,?> tab[] = table;
    //计算hash值
        int hash = key.hashCode();
    //确定检索的位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
    //若key的hash值相等且key相等,则返回true,否则返回false
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return true;
            }
        }
        return false;
    }

2.7,size()方法

代码语言:javascript
复制
public synchronized int size() {
     //返回key的数量,有多少key,就有多少entry
        return count;
    }

2.8,isEmpty()方法

代码语言:javascript
复制
public synchronized boolean isEmpty() {
    //若hashTable的元素个数为0,则表明为空
        return count == 0;
    }

2.9,remove()方法

代码语言:javascript
复制
public synchronized V remove(Object key) {
     //将table引用赋值给临时变量tab[]
        Entry<?,?> tab[] = table;
     //计算key的hash值就是快速找到key的位置
        int hash = key.hashCode();
     //确定key的位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            //找到了key
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                if (prev != null) {
                    //断开链接
                    prev.next = e.next;
                } else {
                    //此时tab[index]置为了null
                    tab[index] = e.next;
                }
                //元素个数减一
                count--;
                //返回待删除的元素
                V oldValue = e.value;
                //等待某个时刻,触发gc
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

2.10,clear()方法

代码语言:javascript
复制
public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
    //循环,将table[]的每一个元素置为null
    //成员变量count置为0,等待gc在某个时刻被触发,将堆上的
    //不可达对象进行回收,进行内存碎片的清理
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

2.11,getOrDefault()方法

代码语言:javascript
复制
public synchronized V getOrDefault(Object key, V defaultValue) {
     //复用原来的get()方法,若元素key不存在,则返回null
        V result = get(key);
     //若为null,则使用默认值
        return (null == result) ? defaultValue : result;
    }

2.12,replace()方法

代码语言:javascript
复制
public synchronized V replace(K key, V value) {
        Objects.requireNonNull(value);
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
     //得到key所在的位置index
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
     //循环判断每一个entry数据,是否hash值以及key和传入的key相等
     //若相等,则使用新值value替换查找到的旧值
        for (; e != null; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        return null;
    }

三,总结一下

其实,这些方法分析下来,难度是有的,但是整体分析下来还是比较值得的,如果我自己只分析,不记录文字输出出来应该很快就会分析完,内容输出来确实可以帮助自己一些,如果对别人有一点点启发何尝不是一件正确的事情呢,这里自己分析了大部分的常用的方法,极个别的方法没有分析,用的比较少,为什么hashtable是线程安全的?想必看过整篇文章就明白了,目前自己不会很详细的把每个知识点都说到,毕竟一篇文章是说不完的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一,HashTable源码分析
  • 二,方法分析
    • 2.1,构造函数
    • 2.2,put()方法
    • 2.3,get()方法
    • 2.4,contains()方法
    • 2.5,containsValue()方法
    • 2.6,containsKey()方法
    • 2.7,size()方法
    • 2.8,isEmpty()方法
    • 2.9,remove()方法
    • 2.10,clear()方法
    • 2.11,getOrDefault()方法
    • 2.12,replace()方法
  • 三,总结一下
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档