1.Redis7.0源码阅读:哈希表扩容、源码缩容以及rehash
2.HashMapåHashtableçåºå«
3.hashmapåºå±å®ç°åç
4.hash / hashtable(linux kernel 哈希表)
5.Hashtableï¼HashMapåTreeMapçåºå«
6.String源码分析(1)--哈希篇
Redis7.0源码阅读:哈希表扩容、源码缩容以及rehash
当哈希值相同发生冲突时,源码Redis 源码使用链表法解决,将冲突的源码键值对通过链表连接,但随着数据量增加,源码a8源码论坛冲突加剧,源码查找效率降低。源码负载因子衡量冲突程度,源码负载因子越大,源码冲突越严重。源码为优化性能,源码Redis 源码需适时扩容,将新增键值对放入新哈希桶,源码减少冲突。源码
扩容发生在 setCommand 部分,sta公式源码其中 dictKeyIndex 获取键值对索引,判断是否需要扩容。_dictExpandIfNeeded 函数执行扩容逻辑,条件包括:不在 rehash 过程中,哈希表初始大小为0时需扩容,或负载因子大于1且允许扩容或负载因子超过阈值。
扩容大小依据当前键值对数量计算,如哈希表长度为4,实际有9个键值对,扩容至(最小的2的n次幂大于9)。子进程存在时,dict_can_resize 为0,反之为1。fork 子进程用于写时复制,确保持久化操作的分享红包源码稳定性。
哈希表缩容由 tryResizeHashTables 判断负载因子是否小于0.1,条件满足则重新调整大小。此操作在数据库定时检查,且无子进程时执行。
rehash 是为解决链式哈希效率问题,通过增加哈希桶数量分散存储,减少冲突。dictRehash 函数完成这一任务,移动键值对至新哈希表,使用位运算优化哈希计算。渐进式 rehash 通过分步操作,减少响应时间,适应不同负载情况。定时任务检测服务器空闲时,进行大步挪动哈希桶。2009春晚源码
在 rehash 过程中,数据查询首先在原始哈希表进行,若未找到,则在新哈希表中查找。rehash 完成后,哈希表结构调整,原始表指向新表,新表内容返回原始表,实现 rehash 结果的整合。
综上所述,Redis 通过哈希表的扩容、缩容以及 rehash 动态调整哈希桶大小,优化查找效率,确保数据存储与检索的高效性。这不仅提高了 Redis 翻译源码下载的性能,也为复杂数据存储与管理提供了有力支持。
HashMapåHashtableçåºå«
HashMapåHashtableçæ¯è¾æ¯Javaé¢è¯ä¸ç常è§é®é¢ï¼ç¨æ¥èéªç¨åºåæ¯å¦è½å¤æ£ç¡®ä½¿ç¨éå类以åæ¯å¦å¯ä»¥éæºåºå使ç¨å¤ç§æ路解å³é®é¢ãHashMapçå·¥ä½åçãArrayListä¸Vectorçæ¯è¾ä»¥åè¿ä¸ªé®é¢æ¯æå ³Java éåæ¡æ¶çæç»å ¸çé®é¢ãHashtableæ¯ä¸ªè¿æ¶çéåç±»ï¼åå¨äºJava APIä¸å¾ä¹ äºãå¨Java 4ä¸è¢«éåäºï¼å®ç°äºMapæ¥å£ï¼æ以èªæ¤ä»¥åä¹æäºJavaéåæ¡æ¶ä¸çä¸é¨åãHashtableåHashMapå¨Javaé¢è¯ä¸ç¸å½å®¹æ被é®å°ï¼çè³æ为äºéåæ¡æ¶é¢è¯é¢ä¸æ常被èçé®é¢ï¼æ以å¨åå ä»»ä½Javaé¢è¯ä¹åï¼é½ä¸è¦å¿äºåå¤è¿ä¸é¢ã
è¿ç¯æç« ä¸ï¼æ们ä¸ä» å°ä¼çå°HashMapåHashtableçåºå«ï¼è¿å°çå°å®ä»¬ä¹é´çç¸ä¼¼ä¹å¤ã
HashMapåHashtableé½å®ç°äºMapæ¥å£ï¼ä½å³å®ç¨åªä¸ä¸ªä¹åå è¦å¼æ¸ æ¥å®ä»¬ä¹é´çåå«ã主è¦çåºå«æï¼çº¿ç¨å®å ¨æ§ï¼åæ¥(synchronization)ï¼ä»¥åé度ã
ç±äºHashtableæ¯çº¿ç¨å®å ¨çä¹æ¯synchronizedï¼æ以å¨å线ç¨ç¯å¢ä¸å®æ¯HashMapè¦æ ¢ãå¦æä½ ä¸éè¦åæ¥ï¼åªéè¦åä¸çº¿ç¨ï¼é£ä¹ä½¿ç¨HashMapæ§è½è¦å¥½è¿Hashtableã
HashMapä¸è½ä¿è¯éçæ¶é´çæ¨ç§»Mapä¸çå ç´ æ¬¡åºæ¯ä¸åçã
fail-fastæºå¶å¨éåä¸ä¸ªéåæ¶ï¼å½éåç»æ被修æ¹ï¼ä¼æåºConcurrent Modification Exceptionã
fail-fastä¼å¨ä»¥ä¸ä¸¤ç§æ åµä¸æåºConcurrentModificationException
éå被å建åï¼å¨éåå®çè¿ç¨ä¸ä¿®æ¹äºç»æã
注æ remove()æ¹æ³ä¼è®©expectModcountåmodcount ç¸çï¼æ以æ¯ä¸ä¼æåºè¿ä¸ªå¼å¸¸ã
å½ä¸ä¸ªçº¿ç¨å¨éåè¿ä¸ªéåï¼èå¦ä¸ä¸ªçº¿ç¨å¯¹è¿ä¸ªéåçç»æè¿è¡äºä¿®æ¹ã
è¿ä»£å¨å¨éåè¿ç¨ä¸æ¯ç´æ¥è®¿é®å é¨æ°æ®çï¼å æ¤å é¨çæ°æ®å¨éåçè¿ç¨ä¸æ æ³è¢«ä¿®æ¹ã为äºä¿è¯ä¸è¢«ä¿®æ¹ï¼è¿ä»£å¨å é¨ç»´æ¤äºä¸ä¸ªæ è®° âmodeâ ï¼å½éåç»ææ¹åï¼æ·»å å é¤æè ä¿®æ¹ï¼ï¼æ è®°"mode"ä¼è¢«ä¿®æ¹ï¼èè¿ä»£å¨æ¯æ¬¡çhasNext()ånext()æ¹æ³é½ä¼æ£æ¥è¯¥"mode"æ¯å¦è¢«æ¹åï¼å½æ£æµå°è¢«ä¿®æ¹æ¶ï¼æåºConcurrent Modification Exceptionã
ä¸é¢ççArrayListè¿ä»£å¨é¨åçæºç ã
å¯ä»¥çå°å®çæ è®°âmodeâ为 expectedModeCountã
fail-safeä»»ä½å¯¹éåç»æçä¿®æ¹é½ä¼å¨ä¸ä¸ªå¤å¶çéåä¸è¿è¡ä¿®æ¹ï¼å æ¤ä¸ä¼æåºConcurrentModificationExceptionã
fail-safeæºå¶æ两个é®é¢
HashMapå¯ä»¥éè¿ä¸é¢çè¯å¥è¿è¡åæ¥ï¼
Map m = Collections.synchronizeMap(hashMap);
HashtableåHashMapæå 个主è¦çä¸åï¼çº¿ç¨å®å ¨ä»¥åé度ãä» å¨ä½ éè¦å®å ¨ç线ç¨å®å ¨çæ¶å使ç¨Hashtableï¼èå¦æä½ ä½¿ç¨Java 5æ以ä¸çè¯ï¼è¯·ä½¿ç¨ConcurrentHashMapå§ã
hashmapåºå±å®ç°åç
hashmapåºå±å®ç°åçæ¯SortedMapæ¥å£è½å¤æå®ä¿åçè®°å½æ ¹æ®é®æåºï¼é»è®¤æ¯æé®å¼çååºæåºï¼ä¹å¯ä»¥æå®æåºçæ¯è¾å¨ï¼å½ç¨IteratoréåTreeMapæ¶ï¼å¾å°çè®°å½æ¯æè¿åºçãå¦æ使ç¨æåºçæ å°ï¼å»ºè®®ä½¿ç¨TreeMapãå¨ä½¿ç¨TreeMapæ¶ï¼keyå¿ é¡»å®ç°Comparableæ¥å£æè å¨æé TreeMapä¼ å ¥èªå®ä¹çComparatorï¼å¦åä¼å¨è¿è¡æ¶æåºjava.lang.ClassCastExceptionç±»åçå¼å¸¸ã
Hashtableæ¯éçç±»ï¼å¾å¤æ å°ç常ç¨åè½ä¸HashMap类似ï¼ä¸åçæ¯å®æ¿èªDictionaryç±»ï¼å¹¶ä¸æ¯çº¿ç¨å®å ¨çï¼ä»»ä¸æ¶é´åªæä¸ä¸ªçº¿ç¨è½åHashtable
ä»ç»æå®ç°æ¥è®²ï¼HashMapæ¯ï¼æ°ç»+é¾è¡¨+红é»æ ï¼JDK1.8å¢å äºçº¢é»æ é¨åï¼å®ç°çã
æ©å±èµæ
ä»æºç å¯ç¥ï¼HashMapç±»ä¸æä¸ä¸ªé常éè¦çå段ï¼å°±æ¯ Node[] tableï¼å³åå¸æ¡¶æ°ç»ãNodeæ¯HashMapçä¸ä¸ªå é¨ç±»ï¼å®ç°äºMap.Entryæ¥å£ï¼æ¬è´¨æ¯å°±æ¯ä¸ä¸ªæ å°(é®å¼å¯¹)ï¼é¤äºKï¼Vï¼è¿å å«hashånextã
HashMapå°±æ¯ä½¿ç¨åå¸è¡¨æ¥åå¨çãåå¸è¡¨ä¸ºè§£å³å²çªï¼éç¨é¾å°åæ³æ¥è§£å³é®é¢ï¼é¾å°åæ³ï¼ç®åæ¥è¯´ï¼å°±æ¯æ°ç»å é¾è¡¨çç»åãå¨æ¯ä¸ªæ°ç»å ç´ ä¸é½ä¸ä¸ªé¾è¡¨ç»æï¼å½æ°æ®è¢«Hashåï¼å¾å°æ°ç»ä¸æ ï¼ææ°æ®æ¾å¨å¯¹åºä¸æ å ç´ çé¾è¡¨ä¸ã
å¦æåå¸æ¡¶æ°ç»å¾å¤§ï¼å³ä½¿è¾å·®çHashç®æ³ä¹ä¼æ¯è¾åæ£ï¼å¦æåå¸æ¡¶æ°ç»æ°ç»å¾å°ï¼å³ä½¿å¥½çHashç®æ³ä¹ä¼åºç°è¾å¤ç¢°æï¼æ以就éè¦å¨ç©ºé´ææ¬åæ¶é´ææ¬ä¹é´æè¡¡ï¼å ¶å®å°±æ¯å¨æ ¹æ®å®é æ åµç¡®å®åå¸æ¡¶æ°ç»ç大å°ï¼å¹¶å¨æ¤åºç¡ä¸è®¾è®¡å¥½çhashç®æ³åå°Hash碰æã
hash / hashtable(linux kernel 哈希表)
哈希表,或称为散列表,是一种高效的数据结构,因其插入和查找速度的优势而备受关注。然而,其空间利用率并不固定,需要权衡。让我们通过实例来深入理解它的作用和工作原理。
想象一个场景:我们需要高效地存储和访问大量数据。首先,常规的数组方法,如普通数组和有序数组,虽然插入简单,但查找效率低,尤其是在数据量较大时。例如,查找可能需要对数千个元素进行比较。有序数组通过牺牲增删效率来提升查询,但数组空间固定且可能浪费大量资源。
链表提供了更灵活的增删操作,但随机访问困难,适合数据频繁变动的情况。红黑树在查询和增删效率上表现优秀,但此处暂不讨论。庞大的数组虽然理论上能快速查找,但实际操作中难以实现,因为它需要预先预估并准备极大数据空间。
这时,哈希表登场了。它利用哈希函数将数据映射到一个较小的数组中,即使存在冲突(不同数据映射到同一地址),通过链表解决,仍然能显著提升查找效率。例如,即使身份证号的哈希结果可能有重复,但实际冲突相对较少,通过链表链接,平均查找次数大大减少。
使用哈希表包括简单的步骤:包含头文件,声明和初始化哈希表,添加节点,以及通过哈希键查找节点。在实际源码中,如Linux kernel的hash.h和hashtable.h文件,哈希表的初始化和操作都是基于这些步骤进行的。
总结来说,哈希表在大数据场景中通过计算直接定位数据,显著提高效率,尤其是在数据量增大时。如果你对Linux kernel的哈希表实现感兴趣,可以关注我的专栏RTFSC,深入探讨更多源码细节。
Hashtableï¼HashMapåTreeMapçåºå«
Java为æ°æ®ç»æä¸çæ å°å®ä¹äºä¸ä¸ªæ¥å£java.util.Mapï¼å®æå个å®ç°ç±»ï¼åå«æ¯HashMapãHashTableãLinkedHashMapåTreeMapã
è¿éä»ç»è¿4ä¸å®ä¾çç¨æ³ååºå«ã
å ³é®ææ¯åæï¼
Mapç¨äºåå¨é®å¼å¯¹ï¼æ ¹æ®é®å¾å°å¼ï¼å æ¤ä¸å 许é®éå¤ï¼å¼å¯ä»¥éå¤ã
l ï¼1ï¼HashMapæ¯ä¸ä¸ªæ常ç¨çMapï¼å®æ ¹æ®é®çhashCodeå¼åå¨æ°æ®ï¼æ ¹æ®é®å¯ä»¥ç´æ¥è·åå®çå¼ï¼å ·æå¾å¿«ç访é®é度ãHashMapæå¤åªå 许ä¸æ¡è®°å½çé®ä¸ºnullï¼ä¸å 许å¤æ¡è®°å½çå¼ä¸ºnullãHashMapä¸æ¯æ线ç¨çåæ¥ï¼å³ä»»ä¸æ¶å»å¯ä»¥æå¤ä¸ªçº¿ç¨åæ¶åHashMapï¼å¯è½ä¼å¯¼è´æ°æ®çä¸ä¸è´ãå¦æéè¦åæ¥ï¼å¯ä»¥ç¨Collections.synchronizedMap(HashMap map)æ¹æ³ä½¿HashMapå ·æåæ¥çè½åã
l ï¼2ï¼Hashtableä¸HashMap类似ï¼ä¸åçæ¯ï¼å®ä¸å 许记å½çé®æè å¼ä¸ºç©ºï¼å®æ¯æ线ç¨çåæ¥ï¼å³ä»»ä¸æ¶å»åªæä¸ä¸ªçº¿ç¨è½åHashtableï¼ç¶èï¼è¿ä¹å¯¼è´äºHashtableå¨åå ¥æ¶ä¼æ¯è¾æ ¢ã
l ï¼3ï¼LinkedHashMapä¿åäºè®°å½çæå ¥é¡ºåºï¼å¨ç¨IteraoréåLinkedHashMapæ¶ï¼å å¾å°çè®°å½è¯å®æ¯å æå ¥çãå¨éåçæ¶åä¼æ¯HashMapæ ¢ãæHashMapçå ¨é¨ç¹æ§ã
l ï¼4ï¼TreeMapè½å¤æå®ä¿åçè®°å½æ ¹æ®é®æåºï¼é»è®¤æ¯æååºæåºï¼ä¹å¯ä»¥æå®æåºçæ¯è¾å¨ãå½ç¨IteraoréåTreeMapæ¶ï¼å¾å°çè®°å½æ¯æè¿åºçãTreeMapçé®åå¼é½ä¸è½ä¸ºç©ºã
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class TestMap {
public static void init(Map map){
if (map != null){
String key = null;
for (int i=5; i>0; i--){
key = new Integer(i).toString() + ".0";
map.put(key, key.toString());
//Mapä¸çé®æ¯ä¸éå¤çï¼å¦ææå ¥ä¸¤ä¸ªé®å¼ä¸æ ·çè®°å½ï¼
//é£ä¹åæå ¥çè®°å½ä¼è¦çå æå ¥çè®°å½
map.put(key, key.toString() + "0"); }
}
}
public static void output(Map map){
if (map != null){
Object key = null;
Object value = null;
//使ç¨è¿ä»£å¨éåMapçé®ï¼æ ¹æ®é®åå¼
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
//æè 使ç¨è¿ä»£å¨éåMapçè®°å½Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//ä¸ä¸ªMap.Entry代表ä¸æ¡è®°å½
entry = (Map.Entry)it.next();
//éè¿entryå¯ä»¥è·å¾è®°å½çé®åå¼
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
}
}
}
public static boolean containsKey(Map map, Object key){
if (map != null){
return map.containsKey(key);
}
return false;
}
public static boolean containsValue(Map map, Object value){
if (map != null){
return map.containsValue(value);
}
return false;
}
public static void testHashMap(){
Map myMap = new HashMap();
init(myMap);
//HashMapçé®å¯ä»¥ä¸ºnull
myMap.put(null,"ddd");
//HashMapçå¼å¯ä»¥ä¸ºnull
myMap.put("aaa", null);
output(myMap);
}
public static void testHashtable(){
Map myMap = new Hashtable();
init(myMap);
//Hashtableçé®ä¸è½ä¸ºnull
//myMap.put(null,"ddd");
//Hashtableçå¼ä¸è½ä¸ºnull
//myMap.put("aaa", null);
output(myMap);
}
public static void testLinkedHashMap(){
Map myMap = new LinkedHashMap();
init(myMap);
//LinkedHashMapçé®å¯ä»¥ä¸ºnull
myMap.put(null,"ddd");
myMap.put(null,"aaa");
//LinkedHashMapçå¼å¯ä»¥ä¸ºnull
myMap.put("aaa", null);
output(myMap);
}
public static void testTreeMap(){
Map myMap = new TreeMap();
init(myMap);
//TreeMapçé®ä¸è½ä¸ºnull
//myMap.put(null,"ddd");
//TreeMapçå¼ä¸è½ä¸ºnull
//myMap.put("aaa", null);
output(myMap);
}
public static void main(String[] args) {
System.out.println("éç¨HashMap");
TestMap.testHashMap();
System.out.println("éç¨Hashtable");
TestMap.testHashtable();
System.out.println("éç¨LinkedHashMap");
TestMap.testLinkedHashMap();
System.out.println("éç¨TreeMap");
TestMap.testTreeMap();
Map myMap = new HashMap();
TestMap.init(myMap);
System.out.println("æ°åå§åä¸ä¸ªMap: myMap");
TestMap.output(myMap);
//æ¸ ç©ºMap
myMap.clear();
System.out.println("å°myMap clearåï¼myMap空äºä¹? " + myMap.isEmpty());
TestMap.output(myMap);
myMap.put("aaa", "aaaa");
myMap.put("bbb", "bbbb");
//å¤æMapæ¯å¦å å«æé®æè æå¼
System.out.println("myMapå å«é®aaa? "+ TestMap.containsKey(myMap, "aaa"));
System.out.println("myMapå å«å¼aaaa? "+ TestMap.containsValue(myMap, "aaaa"));
//æ ¹æ®é®å é¤Mapä¸çè®°å½
myMap.remove("aaa");
System.out.println("å é¤é®aaaåï¼myMapå å«é®aaa? "+ TestMap.containsKey(myMap, "aaa"));
//è·åMapçè®°å½æ°
System.out.println("myMapå å«çè®°å½æ°: " + myMap.size());
}
}
è¾åºç»æï¼
éç¨HashMap
key: null; value: ddd
key: 3.0; value: 3.
key: aaa; value: null
key: 4.0; value: 4.
key: 1.0; value: 1.
key: 5.0; value: 5.
key: 2.0; value: 2.
éç¨Hashtable
key: 4.0; value: 4.
key: 1.0; value: 1.
key: 3.0; value: 3.
key: 5.0; value: 5.
key: 2.0; value: 2.
éç¨LinkedHashMap
key: 5.0; value: 5.
key: 4.0; value: 4.
key: 3.0; value: 3.
key: 2.0; value: 2.
key: 1.0; value: 1.
key: null; value: aaa
key: aaa; value: null
éç¨TreeMap
key: 1.0; value: 1.
key: 2.0; value: 2.
key: 3.0; value: 3.
key: 4.0; value: 4.
key: 5.0; value: 5.
æ°åå§åä¸ä¸ªMap: myMap
key: 3.0; value: 3.
key: 4.0; value: 4.
key: 1.0; value: 1.
key: 5.0; value: 5.
key: 2.0; value: 2.
å°myMap clearåï¼myMap空äºä¹? true
myMapå å«é®aaa? true
myMapå å«å¼aaaa? true
å é¤é®aaaåï¼myMapå å«é®aaa? false
myMapå å«çè®°å½æ°: 1
æºç åæï¼
éåMapæ两ç§æ¹æ³ï¼
ï¼1ï¼mapçkeySet()æ¹æ³è·å¾é®çéåï¼åè°ç¨é®éåçiteratoræ¹æ³è·å¾é®çè¿ä»£å¨ï¼ä»¥æ¤è¿ä»£å°ååºMapä¸çé®ï¼ç¨getæ¹æ³è·å¾é®å¯¹åºçå¼ï¼ä¾¿å®æäºMapçéåã代ç å¦ä¸æ示ï¼
//使ç¨è¿ä»£å¨éåMapçé®ï¼æ ¹æ®é®åå¼
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
ï¼2ï¼ä½¿ç¨MapçentrySetæ¹æ³è·å¾Mapä¸è®°å½çéåï¼æ¯æ¡å¯¹è±¡é½æ¯ä¸ä¸ªMap.Entry对象ï¼ä½¿ç¨å ¶getKeyæ¹æ³è·å¾è®°å½çé®ï¼ä½¿ç¨å ¶getValueæ¹æ³è·å¾è®°å½çå¼ã代ç å¦ä¸æ示ï¼
//æè 使ç¨è¿ä»£å¨éåMapçè®°å½Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//ä¸ä¸ªMap.Entry代表ä¸æ¡è®°å½
entry = (Map.Entry)it.next();
//éè¿entryå¯ä»¥è·å¾è®°å½çé®åå¼
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
String源码分析(1)--哈希篇
本文基于JDK1.8,从Java中==符号的使用开始,解释了它判断的是对象的内存地址而非内容是否相等。接着,通过分析String类的equals()方法实现,说明了在比较字符串时,应使用equals()而非==,因为equals()方法可以准确判断字符串内容是否相等。
深入探讨了String类作为“值类”的特性,即它需要覆盖Object类的equals()方法,以满足比较字符串时逻辑上相等的需求。同时,强调了在覆盖equals()方法时也必须覆盖hashCode()方法,以确保基于散列的集合(如HashMap、HashSet和Hashtable)可以正常工作。解释了哈希码(hashcode)在将不同的输入映射成唯一值中的作用,以及它与字符串内容的关系。
在分析String类的hashcode()方法时,介绍了计算哈希值的公式,包括使用这个奇素数的原因,以及其在计算性能上的优势。进一步探讨了哈希碰撞的概念及其产生的影响,提出了防止哈希碰撞的有效方法之一是扩大哈希值的取值空间,并介绍了生日攻击这一概念,解释了它如何在哈希空间不足够大时制造碰撞。
最后,总结了哈希碰撞与散列表性能的关系,以及在满足安全与成本之间找到平衡的重要性。提出了确保哈希值的最短长度的考虑因素,并提醒读者在理解和学习JDK源码时,可以关注相关公众号以获取更多源码分析文章。