zoukankan      html  css  js  c++  java
  • HashTable —— 线程安全的散列表

    HashTable 认识:

    1. 底层使用散列表,存贮键值对,键值非null
    2. 使用synchronize 保证线程安全
    3. 如果多线程高发量,推荐使用 concurrentHashMap; 如无需多线程,可使用 HashMap

    ■ 重要全局变量

    //The hash table data.
    //底层维护一个Entry(键值对)数组
    private transient Entry<K,V>[] table;
    //The total number of entries in the hash table. //元素总量,等同于HashMap的size private transient int count;
    //The load factor for the hashtable. //负载因子 private float loadFactor;
    /** * The table is rehashed when its size exceeds this threshold. * (The value of this field is (int)(capacity * loadFactor).) * 超过阈值进行rehash */ private int threshold; /** * The number of times this Hashtable has been structurally modified * Structural modifications are those that change the number of entries in * the Hashtable or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the Hashtable fail-fast. (See ConcurrentModificationException). * 结构性变动时modCount计数+1,用于遍历时的fail-fast机制生效 */ private transient int modCount = 0

    ■ 构造函数

    /**
         * Constructs a new, empty hashtable with the specified initial
         * capacity and the specified load factor.
         */
        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;    //没有强调2次幂,cap为0时,默认是1
            this.loadFactor = loadFactor;
            table = new Entry<?,?>[initialCapacity];
            threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        }
    
       
        /**
         * Constructs a new, empty hashtable with a default initial capacity (11)
         * and load factor (0.75).
         * 容量11,负载因子 0.75
         */
        public Hashtable() {
            this(11, 0.75f);
        }    

     ■主要方法 (基本都是 syn 修饰)

       - put(K key, V value)

    public synchronized V put(K key, V value) {
        // Make sure the value is not null,而HashMap选择将key为null永远存放为table[0]位置
        if (value == null) {
            throw new NullPointerException();
        }
        // Makes sure the key is not already in the hashtable.
        //确保key不在hashtable中
        //首先,通过hash方法计算key的哈希值,并计算得出index值,确定其在table[]中的位置
        //其次,迭代index索引位置的链表,如果该位置处的链表存在相同的key,则替换value,返回旧的value
        Entry tab[] = table;
        int hash = hash(key);
        //计算下标,这里使用%方法,性能远不及HashMap的位运算 (这也是不推荐使用HashTable的原因之一)
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            //直接使用equals比较,而HashMap多了一层地址比较 `((k = e.key) == key || key.equals(k))`
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }
    //结构性变更操作 modCount计数+1 modCount++; //HashMap选择新增addEntry方法封装一下逻辑 if (count >= threshold) { // Rehash the table if the threshold is exceeded //如果超过阀值,就进行rehash操作 rehash(); tab = table; hash = hash(key); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. //将值插入,返回的为null Entry<K,V> e = tab[index]; // 创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置e为新的Entry的下一个元素 tab[index] = new Entry<>(hash, key, value, e); //size++ count++; return null; }

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

     ■ HashTable 遍历

    /**
     * Created by roman on 2017/10/03.
     *【2017 java's day】
     */
    public class ListHashTableTest {
        public static void main(String[] args) {
            Hashtable<String, Object> hb = new Hashtable<String, Object>();
            hb.put("1", "Java");
            hb.put("flag", true);
            hb.put("age", 44);
    
            Iterator iterator = hb.keySet().iterator();
            iterator.forEachRemaining(value -> System.out.println(hb.get(value)));  //使用了lamda
        }
    }

     ■ HashTable VS HashMap

    • HashTable 基于 Dictionary 类,而 HashMap 是基于 AbstractMap。Dictionary 是任何可将键映射到相应值的类的抽象父类,而 AbstractMap 是基于 Map 接口的实现,它以最大限度地减少实现此接口所需的工作.
    • HashMap 的 key 和 value 都允许为 null,而 Hashtable 的 key 和 value 都不允许为 null。HashMap 遇到 key 为 null 的时候,调用 putForNullKey 方法进行处理(统一放入table[0]位置),而对 value 没有处理;Hashtable遇到 null,直接返回 NullPointerException.
    • Hashtable 方法是同步,而HashMap则不是。Hashtable 中的几乎所有的 public 的方法都是 synchronized 的,而有些方法也是在内部通过 synchronized 代码块来实现。
    • HashTable由于使用sync和%运算(以及相关算法实现)的缘故,相比于HashMap,性能较低,因此非常不推荐继续使用HashTable。
    • 非竞争环境下推荐使用HashMap。
    • 多线程环境下推荐使用ConcurrentHashMap。
  • 相关阅读:
    (转)50道JavaScript基础面试题(附答案)
    (转)44 道 JavaScript 难题
    web本地存储(localStorage、sessionStorage)
    (转)在 vue-cli 脚手架中引用 jQuery、bootstrap 以及使用 sass、less 编写 css [vue-cli配置入门]
    (转)Vue 爬坑之路(四)—— 与 Vuex 的第一次接触
    vue 路由传参 params 与 query两种方式的区别
    (转)Vue 爬坑之路(三)—— 使用 vue-router 跳转页面
    【半原创】将js和css文件装入localStorage加速程序执行
    SqlAgent备份脚本
    读<css世界>笔记之img标签
  • 原文地址:https://www.cnblogs.com/romanjoy/p/7251203.html
Copyright © 2011-2022 走看看