zoukankan      html  css  js  c++  java
  • Hashtable源码分析

    简介

    是hash表的一个实现,和HashMap大致相同,存储键值对,任何非空的数据可以作为key或者value。用作key的对象必须实现hashCode和equals方法。
    数据结构为:数组+链表。
    Hashtable是同步的。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的并发实现,那么建议使用ConcurrentHashMap代替Hashtable 。

    类继承关系

    在这里插入图片描述
    实现Map接口,继承Dictionary

    属性

        private transient Entry<?,?>[] table;//hash表数据
        private transient int count;//数据总数
        private int threshold;//扩容阈值
        private float loadFactor;//负载因子
        private transient int modCount = 0;//hash表结构修改次数
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//hash表最大数组长度
    

    内部类

    private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;//单链表
        
        protected Entry(int hash, K key, V value, Entry<K,V> next) {
            this.hash = hash;
            this.key =  key;
            this.value = value;
            this.next = next;
        }
    }
    

    构造方法

    //构造一个空的散列表,默认初始容量(11)和负载因子(0.75)。
    public Hashtable() {
        this(11, 0.75f);
    }
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
    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];//hashtable容量就是给定的容量
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }
    

    添加方法put

     public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }
    
        //确认key不在hash表中
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        // 这里hash & 0x7FFFFFFF之后得到hash本身 没作用
        // hash % tab.length之后 得到hash表低位 小于tab.lenth的一位数
        // 取与之后得到一个小于长度的数字
        int index = (hash & 0x7FFFFFFF) % tab.length;
        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;
    }
    
    private void addEntry(int hash, K key, V value, int index) {
        modCount++;//修改次数
    
        Entry<?,?> tab[] = table;
        //是否需要扩容
        if (count >= threshold) {
            rehash();//扩容table
            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;//计算新的位置
        }
    
        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;
    
        // 扩容规则:扩容一倍再加一
        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循环遍历 外层遍历hash槽 内层遍历链表
        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;
            }
        }
    }
    

    获取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;
    }
    

    移除remove

    public synchronized V remove(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;//定位
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
        	//循环链表找到数据 注意:hashcode不想等 对象必定不想等、hashcode相等 对象有可能相等
            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;
    }
    

    总结

    Hashtable线程安全,但是不推荐使用
    Hashtable数据结构 数组+链表
    定位使用hashcode % 数组长度,有别于hashmap的hashcode & 数组长度-1但是结果一样,注意比对

  • 相关阅读:
    程序员保持快乐活跃的6个好习惯(转)
    Spring MVC Hello World Example(转)
    Oracle定时执行存储过程(转)
    各种常见数据库分页实现(转)
    Linux SSH常用总结(转)
    让人深思......
    void及void指针含义的深刻解析
    UVa 11988
    网络编程学习小结
    Deep Learning(深度学习) 学习笔记(四)
  • 原文地址:https://www.cnblogs.com/paper-man/p/13284622.html
Copyright © 2011-2022 走看看