工作笔记-【Hashtable】和【HashMap】,源码分析

花点时间通过读写方法对Map接口的这三个实现类做出比较:Hashtable,HashMap,ConcurrentHashMap

Hashtable是比较老的实现,HashMap对Hashtable做了优化,ConcurrentHashMap是HashMap的高并发实现。

以Put方法为例,以下是三者的实现及部分注释。

HashMap

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        /*获取Map容量,未初始化(tab==null)则进行初始化*/
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        /*通过hash与容量tabsize做与(&)运算获取index*/
        /*此处与Hashtable不同,后者是取模运算,效率低一些*/
        /*此时如果index处没有数据,则直接创建节点插入*/
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            /*比较index处的头节点(hash、key)*/
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            /*在同一节点处挂的数据达到一定数量时会转化为树结构*/
            /*向树中插入一个节点*/
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            /*遍历联表找到尾结点*/
            /*将数据节点插入到链表尾部或者覆盖节点数据*/
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            /*返回插入结果*/
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        /*如果达到最大容量则扩容,每次扩容为之前2倍*/
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 

可以看出HashMap的主要特点

1. 非线程安全

2. 允许key和value的null值

3. 对Key的哈希值做哈希扩散/二次哈希,减少哈希碰撞

4. 节点数据超过1个使用单向链表存储,超过8个转化为红黑树(JDK1.8优化)。

5. 达到最大容量则扩容,容量为之前2倍。

6. 初始容量为16。

Hashtable

    /*对整个方法做同步*/
    public synchronized V put(K key, V value) {
        /*value不允许为null*/
        if (value == null) {
            throw new NullPointerException();
        }

        Entry<?,?> tab[] = table;
        /*计算key的hash值*/
        int hash = key.hashCode();
        /*将hash值对容量取模运算*/
        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;
    }


    private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        /*如果容量超限,则扩容*/
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

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

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

可以看出Hashtable有以下特点:

1. 线程安全,对整个Put方法同步,写效率较低。

2. 不接受null值

2. 直接哈希并且取模运算找index,比较老的实现,效率不高。

3. 节点数据超过1个使用单向链表保存。

4. 达到最大容量时扩容,扩容后的数据位之前2倍+1。

5. 初始容量为默认11,负载因子0.75. 和HashMap和ConcurrentHashMap不同的是,Hashtable容量可以是任意正整数的值,而另外两个则必须是2的幂数。

ConcurrentHashMap

final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        /*spread做哈希扩散,减少哈希碰撞*/
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            /*初始容量为16*/
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            /*插入新节点,作为表头,加乐观锁(CAS)*/
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            /*正在进行rehash*/
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                /*对单条数据加锁*/
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        /*hash值大于0,链表类型,遍历新增数据或者覆盖旧数据*/
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        /*红黑树,遍历插入新及诶单或者覆盖旧数据*/
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    /*大于阈值(8),转化为树*/
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

 

ConcurrentHashMap是HashMap的高并发实现,除HashMap的特性以外,有以下特点:

1. 线程安全,对单条数据同步,效率高

2. 不接受null值

三者的写操作逻辑区别不大,都是通过哈希值确定节点索引再遍历搜索,仍然是哈希算法和遍历的数据结构方面的区别。

 

此外还有几个相关的Map类型

  • CheckedMap, 对Map的封装,指定和检查传入的参数类型
  • IdentifyHashMap, 使用“引用相等”而非“对象相等”
  • LinkedHashMap,记录元素顺序,可实现LRU,内部使用双向链表,有性能损耗
  • SynchronizedMap,HashMap的同步包装类
  • UnModifiableMap,对Map的只读封装
  • WeakHashMap,存储弱键,内部有个ReferenceQueue,保存被回收的Key,从Map中移除

写的比较简单,仅做记录。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何时候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现场检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可行方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲基、甲基物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值