浅谈HashTable, HashMap, ConcurrentHashMap 之间的区别

简介: 浅谈HashTable, HashMap, ConcurrentHashMap 之间的区别

 大家好,今天为大家带来新的知识,  HashTable, HashMap, ConcurrentHashMap 之间的区别  


目录:

🌸1.回忆hashmap

🌸 2.比较HashTable,ConcurrentHashMap

🌸3.总结HashTable, ConcurrentHashMap的区别

🌸4.一个历史小问题

🌸5.总结HashTable, HashMap, ConcurrentHashMap 之间的区别


1.回忆hashmap


我们之前学过了Hashmap,它的底层是数据结构加链表,这还是一个线程不安全的数据结构

画个图带大家回忆一下HashMap

在添加元素的时候,我们是把每个元素当做一个链表的头结点,然后插入元素.

下面让我们回忆一下以hashmap为基础实现的哈希桶


// 哈希桶的简单实现
// key-value 模型
public class HashBucket {
    private static class Node {
        private int key;
        private int value;
        Node next;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
    private Node[] array;
    private int size;   // 当前的数据个数
    private static final double LOAD_FACTOR = 0.75;
    private static final int DEFAULT_SIZE = 8;//默认桶的大小
    public int put(int key, int value) {
        int index = key % array.length;
        Node cur = array[index];
        while (cur != null) {
            if (cur.key == key) {
                return key;
            }
            cur = cur.next;
        }
        Node node = new Node(key, value);
        node.next = array[index];
        array[index] = node;
        size++;
       double loadFactor=loadFactor();
        if(loadFactor>LOAD_FACTOR){
            resize();
        }
        return -1;
}
    private void resize() {
        //扩容意味着重新哈希
        Node[] newArray=new Node[2*array.length];
        for(int i=0;i<array.length;i++){
            Node cur=array[i];
            while(cur!=null){
                Node curNext=cur.next;//记录一下,因为是在重新哈希,所以下一个结点会出现找不到的情况
                int index=cur.key%newArray.length;
                cur.next=newArray[i];
                newArray[i]=cur;
                cur=curNext;
            }
        }
    }
        // write code here
    //
    //
    private double loadFactor() {
              return size * 1.0 / array.length;
          }
          public int get(int key){
        int index=key%array.length;
        Node cur=array[index];
        while(cur!=null){
            if(cur.key==key){
                return cur.key;
            }
            cur=cur.next;
        }
        return -1;
          }
    }


再回忆一下泛型哈希桶的实现


//泛型哈希桶的实现
public class HashBuck2<K,V> {
    private static class Node<K, V> {
        private K key;
        private V value;
        Node<K, V> next;
        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }
    public Node<K,V>[] array=(Node<K,V>[])new Node[10];
    public int size;
    public void put(K key, V value) {
        int hash = key.hashCode();
        int index = hash % array.length;//自己写引用类型的时候记得equals和hashcode方法
        Node cur = array[index];
        while (cur != null) {
            if (cur.key.equals(key)) {
                return;
            }
            cur = cur.next;
        }
        Node<K, V> node = new Node<>(key, value);
        node.next = array[index];
        array[index] = node;
        size++;
    }
        public V get(K key){
            int hash=key.hashCode();
            int index=hash%array.length;
          Node<K,V> cur =array[index];
            while(cur!=null){
                if(cur.key.equals(key)){
                    return cur.value;
                }
                cur=cur.next;
            }
            return null;
        }
}


2.比较HashTable,ConcurrentHashMap

1.现在再来看一下,HashTable ,HashTable是对整体加锁的,线程是安全的,采用synchronized加锁,来看看图

ed114dd9977744bd99a78ef9f76d1d6d.png

多线程加锁,对于HashMap来说是线程安全的,但是有一个人问题,假设我现在要在1下标和2下标分别插入元素,那么此时是线程安全的吗?答案是yes!但是现在只有一个锁,加锁对象都是this, 那么1和2就会产生锁竞争,就会产生阻塞等待,所以这样的加锁降低效率,那么我们就采用另一种方式,我们采用ConcurrentHashMap,对每个下标都加锁,每次进行操作都是对不同链表的头结点进行加锁,不会用锁冲突,上图

4ec42fb3f111454d9fb4f2e48aea6085.png

我要对1和2同时插入,那么既能保证线程安全还能保证效率高,每次操作都是对不同链表加锁不会产生锁冲突,那么这个时候就可以采用偏向锁来解决,也就是做个标记,必要时再加锁.


2.不仅是加锁方面,还有其他方面的


Concurrent更高效的利用CAS操作进行无锁编程,比如进行和获取和更新操作,就可以使用CAS ,CAS是线程安全的,比锁更高效,但是锁应用范围更广泛

3.优化扩容策略


当哈希表的负载因子达到了阈值,也就是0.75的时候就需要扩容,扩容需要重新申请内存空间,搬运元素,也就是把旧表的元素删除加到新表上,这时候就面临一个问题,如果搬运的元素非常多,那么可能会很卡顿,所以我们需要用到Concurrent来进行搬运,那么它也是会申请一根很大的内存空间,但是不同于HashMap的是,它是一点一点搬运的,速度就会很快,那么此时就相当于有两个哈希表,此时插入元素,去新表插入,删除元素在旧表或者新表,查找的时候新表旧表一起查


3.ConcurrentHahMap和HashTable的区别

1.锁力度不同,ConcurrentHahMap的锁力度更低,并发程度就更高

2.关于CAS,Concurrent更高效利用了CAS原子操作,更加高效

3.优化了扩容策略


4.一个小问题:


ConcurrentHashMap 在 jdk1.8 做了哪些优化?

取消了分段锁 , 直接给每个哈希桶 ( 每个链表 ) 分配了一个锁 ( 就是以每个链表的头结点对象作为锁对

象 ).


5.总结HashTable, HashMap, ConcurrentHashMap 之间的区别

HashtableHashMapConcurrentHashMap 之间的区别?

HashMap: 线程不安全. key 允许为 null

Hashtable: 线程安全. 使用 synchronized 锁 Hashtable 对象, 效率较低. key 不允许为 null.

ConcurrentHashMap: 线程安全. 使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用

CAS 机制. 优化了扩容方式. key 不允许为 null

以上就是今天的分享了,我们下期再见


541d749d4a2147bba059840282d7761e.png

相关文章
|
9月前
|
存储 安全 Java
Java 集合框架中的老炮与新秀:HashTable 和 HashMap 谁更胜一筹?
嗨,大家好,我是技术伙伴小米。今天通过讲故事的方式,详细介绍 Java 中 HashMap 和 HashTable 的区别。从版本、线程安全、null 值支持、性能及迭代器行为等方面对比,帮助你轻松应对面试中的经典问题。HashMap 更高效灵活,适合单线程或需手动处理线程安全的场景;HashTable 较古老,线程安全但性能不佳。现代项目推荐使用 ConcurrentHashMap。关注我的公众号“软件求生”,获取更多技术干货!
142 3
|
10月前
|
安全
HashTable与HashMap的区别
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低 (2)HashTable的Key不允许为null (3)HashTable只对key进行一次hash,HashMap进行了两次Hash (4)HashTable底层使用的数组加链表HashTable与HashMap的区别
155 2
|
11月前
|
存储 开发者
HashMap和Hashtable的key和value可以为null吗,ConcurrentHashMap呢
HashMap的key可以为null,value也可以为null;Hashtable的key不允许为null,value也不能为null;ConcurrentHashMap的key不允许为null
|
9月前
|
存储 安全 Java
如何优雅地回答HashSet与HashMap的区别?看这里!
哈喽,大家好!我是小米,29岁程序员。本文聚焦Java开发中经典的面试题——HashSet和HashMap的区别。HashSet基于HashMap实现,存储唯一值;HashMap存储键值对。两者在数据结构、使用场景、操作方法等方面有显著差异。HashSet无序且依赖元素的hashCode和equals方法保证唯一性,而HashMap需注意线程安全问题。掌握这些知识点,助你轻松应对面试。更多技术干货,欢迎关注我的微信公众号“软件求生”。
238 4
|
11月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
169 1
|
11月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
137 1
|
存储 Java 开发者
HashMap线程安全问题大揭秘:ConcurrentHashMap、自定义同步,一文让你彻底解锁!
【8月更文挑战第24天】HashMap是Java集合框架中不可或缺的一部分,以其高效的键值对存储和快速访问能力广受开发者欢迎。本文深入探讨了HashMap在JDK 1.8后的底层结构——数组+链表+红黑树混合模式,这种设计既利用了数组的快速定位优势,又通过链表和红黑树有效解决了哈希冲突问题。数组作为基石,每个元素包含一个Node节点,通过next指针形成链表;当链表长度过长时,采用红黑树进行优化,显著提升性能。此外,还介绍了HashMap的扩容机制,确保即使在数据量增大时也能保持高效运作。通过示例代码展示如何使用HashMap进行基本操作,帮助理解其实现原理及应用场景。
202 1
|
开发者 C# UED
WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
484 0
|
存储 安全 Java
Hashtable 和 HashMap 的区别
【8月更文挑战第22天】
651 0
|
3月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
187 3