CurrentMap总结

本文详细介绍了ConcurrentHashMap,它是线程安全的Java Map,提供putIfAbsent、remove、replace方法,对比了与HashMap的区别,尤其强调了其Segment和HashEntry结构确保并发安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单介绍CurrentHashMap

ConcurrentMap,它是一个接口,是一个能够支持并发访问的java.util.map集合;

ConcurrentHashMap是一个线程安全,并且是一个高效的HashMap。
spring 缓存注解 通过查看源代码发现将数据存在ConcurrentMap中

Map并发原理

  • ConcurrentMap
    • ConcurrentMap,它是一个接口,是一个能够支持并发访问的java.util.map集合;
    • 在原有java.util.map接口基础上又新提供了4种方法,进一步扩展了原有Map的功能:
    •  public interface ConcurrentMap<K, V> extends Map<K, V> {
      
      //插入元素
      V putIfAbsent(K key, V value);
      
      //移除元素
      boolean remove(Object key, Object value);
      
      //替换元素
      boolean replace(K key, V oldValue, V newValue);
      
      //替换元素
      V replace(K key, V value);
      }
      
      putIfAbsent:与原有put方法不同的是,putIfAbsent方法中如果插入的key相同,则不替换原有的value值;
      remove:与原有remove方法不同的是,新remove方法中增加了对value的判断,如果要删除的key--value不能与Map中原有的key--value对应上,则不会删除该元素;
      replace(K,V,V):增加了对value值的判断,如果key--oldValue能与Map中原有的key--value对应上,才进行替换操作;
      replace(K,V):与上面的replace不同的是,此replace不会对Map中原有的key--value进行比较,如果key存在则直接替换;
      
    • 其实,对于ConcurrentMap来说,我们更关注Map本身的操作,在并发情况下是如何实现数据安全的。在java.util.concurrent包中,ConcurrentMap的实现类主要以ConcurrentHashMap为主。接下来,我们具体来看下。
  • ConcurrentHashMap
    *ConcurrentHashMap是一个线程安全,并且是一个高效的HashMap。
    但是,如果从线程安全的角度来说,HashTable已经是一个线程安全的HashMap,那推出ConcurrentHashMap的意义又是什么呢?
    说起ConcurrentHashMap,就不得不先提及下HashMap在线程不安全的表现,以及HashTable的效率!
  • HashMap
    • 底层数据结构不一样,1.7是数组+链表,1.8则是数组+链表+红黑树结构(当链表长度大于8,转为红黑树)。
      JDK1.8中resize()方法在表为空时,创建表;在表不为空时,扩容;而JDK1.7中resize()方法负责扩容,inflateTable()负责创建表。
       1.8中没有区分键为null的情况,而1.7版本中对于键为null的情况调用putForNullKey()方法。但是两个版本中如果键为null,那么调用hash()方法得到的都将是0,所以键为null的元素都始终位于哈希表table【0】中。
      当1.8中的桶中元素处于链表的情况,遍历的同时最后如果没有匹配的,直接将节点添加到链表尾部;而1.7在遍历的同时没有添加数据,而是另外调用了addEntry()方法,将节点添加到链表头部。
      1.7中新增节点采用头插法,1.8中新增节点采用尾插法。这也是为什么1.8不容易出现环型链表的原因。
      1.7中是通过更改hashSeed值修改节点的hash值从而达到rehash时的链表分散,而1.8中键的hash值不会改变,rehash时根据(hash&oldCap)==0将链表分散。
       1.8rehash时保证原链表的顺序,而1.7中rehash时有可能改变链表的顺序(头插法导致)。
      在扩容的时候:1.7在插入数据之前扩容,而1.8插入数据成功之后扩容。
  • CurrentHashMap
    • 与HashMap不同的是,ConcurrentHashMap中多了一层数组结构,由Segment和HashEntry两个数组组成。其中Segment起到了加锁同步的作用,而HashEntry则起到了存储K.V键值对的作用。在ConcurrentHashMap中,每一个ConcurrentHashMap都包含了一个Segment数组,在Segment数组中每一个Segment对象则又包含了一个HashEntry数组,而在HashEntry数组中,每一个HashEntry对象保存K-V数据的同时又形成了链表结构,此时与HashMap结构相同。在多线程中,每一个Segment对象守护了一个HashEntry数组,当对ConcurrentHashMap中的元素修改时,在获取到对应的Segment数组角标后,都会对此Segment对象加锁,之后再去操作后面的HashEntry元素,这样每一个Segment对象下,都形成了一个小小的HashMap,在保证数据安全性的同时,又提高了同步的效率。只要不是操作同一个Segment对象的话,就不会出现线程等待的问题!
### C语言中的Map实现 在C语言中,标准库并未提供类似于其他高级编程语言(如Python、Java)内置的`map`数据结构。然而,可以通过多种方式手动实现类似的功能。以下是几种常见的方法及其具体实现。 #### 使用数组模拟简单的键值映射 如果键的数量有限且已知范围较小,则可以使用固定大小的数组来存储键值对。这种方法适用于整数类型的键[^1]。 ```c #include <stdio.h> #define MAX_KEYS 100 int main() { int map[MAX_KEYS] = {0}; // 初始化为默认值 int key = 5; int value = 42; map[key] = value; // 插入键值对 printf("Value at key %d is %d\n", key, map[key]); // 获取值 return 0; } ``` 此方法简单高效,但仅限于连续的小型整数作为键。 --- #### 基于哈希表的通用解决方案 对于更复杂的场景,通常采用哈希表技术实现动态扩展的键值映射。这需要定义自定义的数据结构以及相应的操作函数[^4]。 ##### 数据结构设计 下面是一个基于链地址法解决冲突的简易哈希表实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Node { char* key; void* value; struct Node* next; } HashNode; typedef struct { size_t size; HashNode** buckets; } HashMap; // 辅助函数:字符串哈希算法 size_t hash_function(const char* str, size_t table_size) { unsigned long hash = 5381; int c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash % table_size; } // 创建新的HashMap实例 HashMap* create_hash_map(size_t initial_size) { HashMap* map = (HashMap*)malloc(sizeof(HashMap)); map->size = initial_size; map->buckets = (HashNode**)calloc(initial_size, sizeof(HashNode*)); return map; } // 向HashMap插入键值对 void insert_into_map(HashMap* map, const char* key, void* value) { size_t index = hash_function(key, map->size); HashNode* new_node = (HashNode*)malloc(sizeof(HashNode)); new_node->key = strdup(key); // 复制字符串以防修改原始输入 new_node->value = value; new_node->next = NULL; if (!map->buckets[index]) { map->buckets[index] = new_node; } else { HashNode* current = map->buckets[index]; while (current->next != NULL) { current = current->next; } current->next = new_node; } } // 查找指定键对应的值 void* get_from_map(HashMap* map, const char* key) { size_t index = hash_function(key, map->size); HashNode* node = map->buckets[index]; while (node != NULL) { if (strcmp(node->key, key) == 0) { return node->value; } node = node->next; } return NULL; // 如果未找到则返回NULL } // 测试代码 int main() { HashMap* my_map = create_hash_map(10); char* hello_value = "Hello"; char* world_value = "World"; insert_into_map(my_map, "hello", hello_value); insert_into_map(my_map, "world", world_value); printf("%s ", (char*)get_from_map(my_map, "hello")); printf("%s\n", (char*)get_from_map(my_map, "world")); return 0; } ``` 上述代码展示了如何通过哈希表构建一个支持任意字符串键和指针值的映射关系。注意,在实际应用中可能还需要考虑内存管理等问题以避免泄漏或溢出风险[^3]。 --- #### 利用第三方库简化开发过程 除了自己动手编写外,还可以借助成熟的开源项目快速搭建所需的工具集。例如Google提供的[SparseHash](https://github.com/sparsehash/sparsehash),它提供了高效的稀疏散列表现形式供开发者选用[^2]。 --- #### 总结 尽管C语言本身缺乏直接支持的地图类型接口,但是凭借灵活的基础组件仍然能够轻松达成目标需求。无论是基础版还是进阶优化版本都各有优劣之处需视具体情况而定择取合适方案加以运用实践当中去验证效果最佳者为之选也!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值