[{"data":1,"prerenderedAt":1645},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-hashmap-internals":3},{"id":4,"title":5,"body":6,"description":1631,"difficulty":1632,"extension":1633,"framework":1634,"frameworkSlug":90,"meta":1635,"navigation":568,"order":128,"path":1636,"qaPath":1637,"seo":1638,"stem":1639,"subtopic":1640,"topic":1641,"topicSlug":1642,"updated":1643,"__hash__":1644},"blog\u002Fblog\u002Fjava-hashmap-internals.md","How Java HashMap Works Internally — Buckets, Hashing, Resizing & Treeification",{"type":7,"value":8,"toc":1616},"minimark",[9,14,46,50,85,180,198,202,235,327,338,342,362,413,430,434,443,479,486,490,529,620,629,633,650,658,688,692,713,820,849,853,882,1008,1038,1042,1082,1241,1255,1259,1286,1343,1386,1390,1397,1466,1501,1542,1546,1612],[10,11,13],"h2",{"id":12},"why-hashmap-internals-come-up-so-often","Why HashMap internals come up so often",[15,16,17,21,22,26,27,30,31,34,35,26,39,41,42,45],"p",{},[18,19,20],"code",{},"HashMap"," is the data structure interviewers reach for when they want to test whether you\nunderstand a system rather than just an API. On the surface it is a key-value store with\naverage ",[23,24,25],"strong",{},"O(1)"," ",[18,28,29],{},"get"," and ",[18,32,33],{},"put",". Underneath it is a careful balancing act between speed,\nmemory, and worst-case behavior — bit tricks for indexing, a resize policy tuned to a\nmagic constant, and a fallback tree for pathological inputs. This guide walks the whole\nmachine end to end so you can explain not just ",[36,37,38],"em",{},"what",[18,40,20],{}," does but ",[36,43,44],{},"why"," each\ndecision exists.",[10,47,49],{"id":48},"the-bucket-array-and-the-node","The bucket array and the Node",[15,51,52,53,55,56,59,60,63,64,67,68,71,72,71,75,78,79,84],{},"A ",[18,54,20],{}," is built around a single field: an array of buckets, ",[18,57,58],{},"Node\u003CK,V>[] table",". Each\nslot holds the head of a ",[23,61,62],{},"chain"," of entries that landed on the same index. A ",[18,65,66],{},"Node","\npacks four things — the key's already-computed ",[23,69,70],{},"hash",", the ",[23,73,74],{},"key",[23,76,77],{},"value",", and a\n",[23,80,81],{},[18,82,83],{},"next"," pointer that links it to the following entry in the same bucket.",[86,87,92],"pre",{"className":88,"code":89,"language":90,"meta":91,"style":91},"language-java shiki shiki-themes github-light github-dark","static class Node\u003CK,V> {\n  final int hash;   \u002F\u002F cached spread hash, so resize never recomputes it\n  final K key;\n  V value;\n  Node\u003CK,V> next;   \u002F\u002F singly linked chain within one bucket\n}\n","java","",[18,93,94,126,142,150,156,174],{"__ignoreMap":91},[95,96,99,103,106,110,114,117,120,123],"span",{"class":97,"line":98},"line",1,[95,100,102],{"class":101},"szBVR","static",[95,104,105],{"class":101}," class",[95,107,109],{"class":108},"sScJk"," Node",[95,111,113],{"class":112},"sVt8B","\u003C",[95,115,116],{"class":101},"K",[95,118,119],{"class":112},",",[95,121,122],{"class":101},"V",[95,124,125],{"class":112},"> {\n",[95,127,129,132,135,138],{"class":97,"line":128},2,[95,130,131],{"class":101},"  final",[95,133,134],{"class":101}," int",[95,136,137],{"class":112}," hash;   ",[95,139,141],{"class":140},"sJ8bj","\u002F\u002F cached spread hash, so resize never recomputes it\n",[95,143,145,147],{"class":97,"line":144},3,[95,146,131],{"class":101},[95,148,149],{"class":112}," K key;\n",[95,151,153],{"class":97,"line":152},4,[95,154,155],{"class":112},"  V value;\n",[95,157,159,162,164,166,168,171],{"class":97,"line":158},5,[95,160,161],{"class":112},"  Node\u003C",[95,163,116],{"class":101},[95,165,119],{"class":112},[95,167,122],{"class":101},[95,169,170],{"class":112},"> next;   ",[95,172,173],{"class":140},"\u002F\u002F singly linked chain within one bucket\n",[95,175,177],{"class":97,"line":176},6,[95,178,179],{"class":112},"}\n",[15,181,182,183,186,187,190,191,193,194,197],{},"Caching the hash in the node is a small but important optimization: lookups compare the\ncheap ",[18,184,185],{},"int"," hash before ever calling ",[18,188,189],{},"equals",", and resizing can relocate entries without\nre-hashing keys. Everything else in ",[18,192,20],{}," is machinery for choosing ",[36,195,196],{},"which"," bucket a\nnode lives in and keeping the chains short.",[10,199,201],{"id":200},"hashing-and-the-spread-function","Hashing and the spread function",[15,203,204,205,208,209,211,212,215,216,219,220,223,224,226,227,230,231,234],{},"The bucket index is derived from the key's ",[18,206,207],{},"hashCode()",", but ",[18,210,20],{}," never uses it raw.\nBecause indexing keeps only the ",[23,213,214],{},"low bits"," of the hash (more on that below), two keys\nthat differ only in their ",[36,217,218],{},"high"," bits would collide constantly. To defend against weak\n",[18,221,222],{},"hashCode"," implementations, ",[18,225,20],{}," applies a ",[23,228,229],{},"spread"," (or ",[36,232,233],{},"perturbation",") function\nthat mixes the high bits down into the low ones.",[86,236,238],{"className":88,"code":237,"language":90,"meta":91,"style":91},"static final int hash(Object key) {\n  int h;\n  \u002F\u002F XOR the hash with its own top 16 bits — one shift, one XOR\n  return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n}\n",[18,239,240,255,263,268,323],{"__ignoreMap":91},[95,241,242,244,247,249,252],{"class":97,"line":98},[95,243,102],{"class":101},[95,245,246],{"class":101}," final",[95,248,134],{"class":101},[95,250,251],{"class":108}," hash",[95,253,254],{"class":112},"(Object key) {\n",[95,256,257,260],{"class":97,"line":128},[95,258,259],{"class":101},"  int",[95,261,262],{"class":112}," h;\n",[95,264,265],{"class":97,"line":144},[95,266,267],{"class":140},"  \u002F\u002F XOR the hash with its own top 16 bits — one shift, one XOR\n",[95,269,270,273,276,279,283,286,289,292,295,298,301,304,306,309,312,314,317,320],{"class":97,"line":152},[95,271,272],{"class":101},"  return",[95,274,275],{"class":112}," (key ",[95,277,278],{"class":101},"==",[95,280,282],{"class":281},"sj4cs"," null",[95,284,285],{"class":112},") ",[95,287,288],{"class":101},"?",[95,290,291],{"class":281}," 0",[95,293,294],{"class":101}," :",[95,296,297],{"class":112}," (h ",[95,299,300],{"class":101},"=",[95,302,303],{"class":112}," key.",[95,305,222],{"class":108},[95,307,308],{"class":112},"()) ",[95,310,311],{"class":101},"^",[95,313,297],{"class":112},[95,315,316],{"class":101},">>>",[95,318,319],{"class":281}," 16",[95,321,322],{"class":112},");\n",[95,324,325],{"class":97,"line":158},[95,326,179],{"class":112},[15,328,329,330,333,334,337],{},"This costs a single shift and XOR yet meaningfully improves distribution for hashes whose\nentropy lives up high. Notice the spread also defines ",[18,331,332],{},"null","'s hash as ",[18,335,336],{},"0",", which is why a\nnull key always lands in bucket 0 — no special branch needed later.",[10,339,341],{"id":340},"turning-a-hash-into-a-bucket-index","Turning a hash into a bucket index",[15,343,344,345,347,348,353,354,357,358,361],{},"With a spread hash in hand, ",[18,346,20],{}," finds the bucket with ",[23,349,350],{},[18,351,352],{},"index = (n - 1) & hash",",\nwhere ",[18,355,356],{},"n"," is the table length. This is the same result as ",[18,359,360],{},"hash % n"," but far cheaper,\nbecause a bitwise AND is a single CPU instruction while modulo is not.",[86,363,365],{"className":88,"code":364,"language":90,"meta":91,"style":91},"int n = 16;                \u002F\u002F table length\nint idx = (n - 1) & hash;  \u002F\u002F 15 & hash  ->  keeps the low 4 bits\n",[18,366,367,384],{"__ignoreMap":91},[95,368,369,371,374,376,378,381],{"class":97,"line":98},[95,370,185],{"class":101},[95,372,373],{"class":112}," n ",[95,375,300],{"class":101},[95,377,319],{"class":281},[95,379,380],{"class":112},";                ",[95,382,383],{"class":140},"\u002F\u002F table length\n",[95,385,386,388,391,393,396,399,402,404,407,410],{"class":97,"line":128},[95,387,185],{"class":101},[95,389,390],{"class":112}," idx ",[95,392,300],{"class":101},[95,394,395],{"class":112}," (n ",[95,397,398],{"class":101},"-",[95,400,401],{"class":281}," 1",[95,403,285],{"class":112},[95,405,406],{"class":101},"&",[95,408,409],{"class":112}," hash;  ",[95,411,412],{"class":140},"\u002F\u002F 15 & hash  ->  keeps the low 4 bits\n",[15,414,415,416,418,419,421,422,425,426,429],{},"The trick only works because ",[18,417,356],{}," is a power of two. When ",[18,420,356],{}," is a power of two, ",[18,423,424],{},"n - 1"," is\na clean run of all-ones in the low bits (",[18,427,428],{},"0b1111"," for 16), so the AND keeps a contiguous\nslice of the hash and every bucket is reachable. This single line is the reason for the\npower-of-two invariant that shows up everywhere else.",[10,431,433],{"id":432},"why-capacity-is-always-a-power-of-two","Why capacity is always a power of two",[15,435,436,438,439,442],{},[18,437,20],{}," will never let you create a table whose capacity is not a power of two. If you\nask for 10, you get 16; ask for 20, you get 32. The constructor runs ",[18,440,441],{},"tableSizeFor"," to\nround your request up to the next power of two before allocating anything.",[86,444,446],{"className":88,"code":445,"language":90,"meta":91,"style":91},"new HashMap\u003C>(10);  \u002F\u002F actual capacity becomes 16\nnew HashMap\u003C>(20);  \u002F\u002F becomes 32\n",[18,447,448,465],{"__ignoreMap":91},[95,449,450,453,456,459,462],{"class":97,"line":98},[95,451,452],{"class":101},"new",[95,454,455],{"class":112}," HashMap\u003C>(",[95,457,458],{"class":281},"10",[95,460,461],{"class":112},");  ",[95,463,464],{"class":140},"\u002F\u002F actual capacity becomes 16\n",[95,466,467,469,471,474,476],{"class":97,"line":128},[95,468,452],{"class":101},[95,470,455],{"class":112},[95,472,473],{"class":281},"20",[95,475,461],{"class":112},[95,477,478],{"class":140},"\u002F\u002F becomes 32\n",[15,480,481,482,485],{},"The motivation is purely the ",[18,483,484],{},"(n - 1) & hash"," indexing. A power-of-two size guarantees the\nmask is gap-free, so entries spread evenly and the AND substitutes cleanly for modulo. A\nnon-power-of-two size would leave holes in the mask, making some buckets unreachable and\nclustering entries into the rest — exactly what a hash table is supposed to avoid.",[10,487,489],{"id":488},"load-factor-threshold-and-resizing","Load factor, threshold, and resizing",[15,491,52,492,494,495,498,499,502,503,506,507,510,511,514,515,518,519,26,521,524,525,528],{},[18,493,20],{}," starts at capacity ",[23,496,497],{},"16"," with a load factor of ",[23,500,501],{},"0.75",". The load factor is\nthe fullness fraction at which the table grows: the ",[23,504,505],{},"threshold"," is ",[18,508,509],{},"capacity × loadFactor",",\nso a default map resizes once it holds more than ",[18,512,513],{},"16 × 0.75 = 12"," entries. When ",[18,516,517],{},"size","\ncrosses the threshold, ",[18,520,20],{},[23,522,523],{},"doubles"," the capacity and ",[23,526,527],{},"rehashes"," every entry into\nthe bigger table.",[86,530,532],{"className":88,"code":531,"language":90,"meta":91,"style":91},"new HashMap\u003C>();          \u002F\u002F capacity 16, threshold 12\nnew HashMap\u003C>(32, 0.5f);  \u002F\u002F capacity 32, threshold 16\n\n\u002F\u002F the Java 8 split: doubling means each entry either stays at index i\n\u002F\u002F or moves to i + oldCapacity, decided by one new high bit of its hash\nif ((node.hash & oldCapacity) == 0) { \u002F* stays in the \"low\" bucket *\u002F }\nelse                                { \u002F* moves to the \"high\" bucket *\u002F }\n",[18,533,534,544,564,570,575,580,606],{"__ignoreMap":91},[95,535,536,538,541],{"class":97,"line":98},[95,537,452],{"class":101},[95,539,540],{"class":112}," HashMap\u003C>();          ",[95,542,543],{"class":140},"\u002F\u002F capacity 16, threshold 12\n",[95,545,546,548,550,553,556,559,561],{"class":97,"line":128},[95,547,452],{"class":101},[95,549,455],{"class":112},[95,551,552],{"class":281},"32",[95,554,555],{"class":112},", ",[95,557,558],{"class":281},"0.5f",[95,560,461],{"class":112},[95,562,563],{"class":140},"\u002F\u002F capacity 32, threshold 16\n",[95,565,566],{"class":97,"line":144},[95,567,569],{"emptyLinePlaceholder":568},true,"\n",[95,571,572],{"class":97,"line":152},[95,573,574],{"class":140},"\u002F\u002F the Java 8 split: doubling means each entry either stays at index i\n",[95,576,577],{"class":97,"line":158},[95,578,579],{"class":140},"\u002F\u002F or moves to i + oldCapacity, decided by one new high bit of its hash\n",[95,581,582,585,588,590,593,595,597,600,603],{"class":97,"line":176},[95,583,584],{"class":101},"if",[95,586,587],{"class":112}," ((node.hash ",[95,589,406],{"class":101},[95,591,592],{"class":112}," oldCapacity) ",[95,594,278],{"class":101},[95,596,291],{"class":281},[95,598,599],{"class":112},") { ",[95,601,602],{"class":140},"\u002F* stays in the \"low\" bucket *\u002F",[95,604,605],{"class":112}," }\n",[95,607,609,612,615,618],{"class":97,"line":608},7,[95,610,611],{"class":101},"else",[95,613,614],{"class":112},"                                { ",[95,616,617],{"class":140},"\u002F* moves to the \"high\" bucket *\u002F",[95,619,605],{"class":112},[15,621,622,624,625,628],{},[18,623,501],{}," is a deliberate time\u002Fspace trade-off — lower factors waste memory but shorten\nchains, higher factors save memory but lengthen them. Doubling preserves the power-of-two\ninvariant, and because only one new bit decides an entry's fate, Java 8 splits each old\nchain into two ordered sub-chains without recomputing indexes. A resize is still ",[23,626,627],{},"O(n)",",\nwhich is why presizing large maps matters.",[10,630,632],{"id":631},"collisions-and-treeification","Collisions and treeification",[15,634,635,636,638,639,641,642,645,646,649],{},"Distinct keys can map to the same bucket even with perfect ",[18,637,222],{},"s, simply because many\nhashes fold into a small index space. ",[18,640,20],{}," handles these ",[23,643,644],{},"collisions"," by chaining:\noriginally a bucket is a singly linked list. Java 8 added a safety valve — when one bucket's\nchain grows too long, it is converted into a ",[23,647,648],{},"red-black tree"," so lookups within it stay\nlogarithmic instead of linear.",[86,651,656],{"className":652,"code":654,"language":655,"meta":91},[653],"language-text","bucket[5] -> Node(k1) -> Node(k2) -> Node(k3)   \u002F\u002F linked list\n\n\u002F\u002F after treeification:\nbucket[5] -> (balanced red-black tree of TreeNodes)\n","text",[18,657,654],{"__ignoreMap":91},[15,659,660,661,667,668,674,675,26,677,680,681,687],{},"Two thresholds govern this. A chain treeifies at ",[23,662,663,666],{},[18,664,665],{},"TREEIFY_THRESHOLD"," = 8",", but only if\nthe table is already at least ",[23,669,670,673],{},[18,671,672],{},"MIN_TREEIFY_CAPACITY"," = 64","; below that, a long chain\nusually means the table is just too small, so ",[18,676,20],{},[23,678,679],{},"resizes"," instead. A tree reverts\nto a list once it shrinks to ",[23,682,683,686],{},[18,684,685],{},"UNTREEIFY_THRESHOLD"," = 6",". The gap between 8 and 6 is\ndeliberate hysteresis that prevents flip-flopping on repeated add\u002Fremove.",[10,689,691],{"id":690},"the-put-and-get-flow","The put and get flow",[15,693,694,30,696,698,699,701,702,704,705,707,708,712],{},[18,695,33],{},[18,697,29],{}," share the same lookup half. Both spread the key's hash, compute\n",[18,700,484],{},", and then walk the chain. For each node they first compare the cached\n",[23,703,70],{}," (a cheap ",[18,706,185],{}," check that short-circuits most mismatches) and only then call\n",[23,709,710],{},[18,711,189],{}," to confirm the key.",[86,714,716],{"className":88,"code":715,"language":90,"meta":91,"style":91},"map.put(\"a\", 1);  \u002F\u002F empty bucket -> drop node in\nmap.put(\"a\", 2);  \u002F\u002F same key found by hash + equals -> value replaced, returns 1\nmap.put(\"b\", 3);  \u002F\u002F collision -> append to chain (treeify if chain hits 8 and table >= 64)\n\nmap.get(\"a\");     \u002F\u002F hash -> index -> walk chain -> return matching node's value\nmap.get(\"zzz\");   \u002F\u002F no match -> null\n",[18,717,718,742,762,783,787,803],{"__ignoreMap":91},[95,719,720,723,725,728,732,734,737,739],{"class":97,"line":98},[95,721,722],{"class":112},"map.",[95,724,33],{"class":108},[95,726,727],{"class":112},"(",[95,729,731],{"class":730},"sZZnC","\"a\"",[95,733,555],{"class":112},[95,735,736],{"class":281},"1",[95,738,461],{"class":112},[95,740,741],{"class":140},"\u002F\u002F empty bucket -> drop node in\n",[95,743,744,746,748,750,752,754,757,759],{"class":97,"line":128},[95,745,722],{"class":112},[95,747,33],{"class":108},[95,749,727],{"class":112},[95,751,731],{"class":730},[95,753,555],{"class":112},[95,755,756],{"class":281},"2",[95,758,461],{"class":112},[95,760,761],{"class":140},"\u002F\u002F same key found by hash + equals -> value replaced, returns 1\n",[95,763,764,766,768,770,773,775,778,780],{"class":97,"line":144},[95,765,722],{"class":112},[95,767,33],{"class":108},[95,769,727],{"class":112},[95,771,772],{"class":730},"\"b\"",[95,774,555],{"class":112},[95,776,777],{"class":281},"3",[95,779,461],{"class":112},[95,781,782],{"class":140},"\u002F\u002F collision -> append to chain (treeify if chain hits 8 and table >= 64)\n",[95,784,785],{"class":97,"line":152},[95,786,569],{"emptyLinePlaceholder":568},[95,788,789,791,793,795,797,800],{"class":97,"line":158},[95,790,722],{"class":112},[95,792,29],{"class":108},[95,794,727],{"class":112},[95,796,731],{"class":730},[95,798,799],{"class":112},");     ",[95,801,802],{"class":140},"\u002F\u002F hash -> index -> walk chain -> return matching node's value\n",[95,804,805,807,809,811,814,817],{"class":97,"line":176},[95,806,722],{"class":112},[95,808,29],{"class":108},[95,810,727],{"class":112},[95,812,813],{"class":730},"\"zzz\"",[95,815,816],{"class":112},");   ",[95,818,819],{"class":140},"\u002F\u002F no match -> null\n",[15,821,822,824,825,827,828,830,831,834,835,838,839,841,842,845,846,848],{},[18,823,33],{}," adds a node when the chain has no matching key and then bumps ",[18,826,517],{},", triggering a\nresize if the threshold is crossed. ",[18,829,29],{}," is just the lookup with no mutation. In a list\nbucket both are ",[23,832,833],{},"O(chain length)","; in a treeified bucket they are ",[23,836,837],{},"O(log n)",". The match\nstep is why ",[18,840,189],{}," is non-negotiable: landing in the right ",[36,843,844],{},"bucket"," does not mean you\nfound the right ",[36,847,74],{},".",[10,850,852],{"id":851},"the-hashcodeequals-contract","The hashCode\u002Fequals contract",[15,854,855,857,858,860,861,863,864,868,869,873,874,877,878,881],{},[18,856,20],{}," leans directly on the ",[18,859,222],{},"\u002F",[18,862,189],{}," contract: it uses ",[23,865,866],{},[18,867,222],{}," to\nfind the bucket and ",[23,870,871],{},[18,872,189],{}," to find the key inside it. The contract says that if\n",[18,875,876],{},"a.equals(b)",", then ",[18,879,880],{},"a.hashCode() == b.hashCode()"," must hold (the reverse need not — unequal\nobjects may share a hash). Break it by overriding only one method and a key you stored\nbecomes unfindable.",[86,883,885],{"className":88,"code":884,"language":90,"meta":91,"style":91},"class Point {\n  final int x, y;\n  @Override public boolean equals(Object o) {\n    return o instanceof Point p && x == p.x && y == p.y;\n  }\n  @Override public int hashCode() { return Objects.hash(x, y); }  \u002F\u002F always pair these\n}\n",[18,886,887,898,907,934,969,974,1004],{"__ignoreMap":91},[95,888,889,892,895],{"class":97,"line":98},[95,890,891],{"class":101},"class",[95,893,894],{"class":108}," Point",[95,896,897],{"class":112}," {\n",[95,899,900,902,904],{"class":97,"line":128},[95,901,131],{"class":101},[95,903,134],{"class":101},[95,905,906],{"class":112}," x, y;\n",[95,908,909,912,915,918,921,924,927,931],{"class":97,"line":144},[95,910,911],{"class":112},"  @",[95,913,914],{"class":101},"Override",[95,916,917],{"class":101}," public",[95,919,920],{"class":101}," boolean",[95,922,923],{"class":108}," equals",[95,925,926],{"class":112},"(Object ",[95,928,930],{"class":929},"s4XuR","o",[95,932,933],{"class":112},") {\n",[95,935,936,939,942,945,948,951,954,956,959,961,964,966],{"class":97,"line":152},[95,937,938],{"class":101},"    return",[95,940,941],{"class":112}," o ",[95,943,944],{"class":101},"instanceof",[95,946,947],{"class":112}," Point p ",[95,949,950],{"class":101},"&&",[95,952,953],{"class":112}," x ",[95,955,278],{"class":101},[95,957,958],{"class":112}," p.x ",[95,960,950],{"class":101},[95,962,963],{"class":112}," y ",[95,965,278],{"class":101},[95,967,968],{"class":112}," p.y;\n",[95,970,971],{"class":97,"line":158},[95,972,973],{"class":112},"  }\n",[95,975,976,978,980,982,984,987,990,993,996,998,1001],{"class":97,"line":176},[95,977,911],{"class":112},[95,979,914],{"class":101},[95,981,917],{"class":101},[95,983,134],{"class":101},[95,985,986],{"class":108}," hashCode",[95,988,989],{"class":112},"() { ",[95,991,992],{"class":101},"return",[95,994,995],{"class":112}," Objects.",[95,997,70],{"class":108},[95,999,1000],{"class":112},"(x, y); }  ",[95,1002,1003],{"class":140},"\u002F\u002F always pair these\n",[95,1005,1006],{"class":97,"line":608},[95,1007,179],{"class":112},[15,1009,1010,1011,1013,1014,1017,1018,1020,1021,1023,1024,1027,1028,1030,1031,1034,1035,1037],{},"A worse failure is a ",[18,1012,222],{}," that returns a constant — ",[18,1015,1016],{},"return 1;"," for every key sends\nall entries into one bucket, collapsing the map to a single giant chain and making every\noperation ",[23,1019,627],{},". Treeification softens this to ",[23,1022,837],{},", but only when the keys are\nalso ",[18,1025,1026],{},"Comparable","; otherwise the tree falls back to identity-hash ordering. The real cure\nis always a well-distributed ",[18,1029,222],{},". Related: keys should be ",[23,1032,1033],{},"immutable",", because a\nkey's hash is computed at insertion — mutate a field that affects ",[18,1036,222],{}," afterward and\nthe entry becomes a ghost, present in the table but lost in the wrong bucket.",[10,1039,1041],{"id":1040},"null-handling-and-fail-fast-iterators","Null handling and fail-fast iterators",[15,1043,1044,1046,1047,1050,1051,1054,1055,1057,1058,1060,1061,1063,1064,1069,1070,1073,1074,1077,1078,1081],{},[18,1045,20],{}," permits ",[23,1048,1049],{},"one null key"," (special-cased to hash 0, so it sits in bucket 0) and\n",[23,1052,1053],{},"any number of null values",". The catch is that ",[18,1056,29],{}," returns ",[18,1059,332],{}," both for an absent\nkey and a key explicitly mapped to ",[18,1062,332],{}," — use ",[23,1065,1066],{},[18,1067,1068],{},"containsKey"," to distinguish them.\nIterators are ",[23,1071,1072],{},"fail-fast",": they track a ",[18,1075,1076],{},"modCount"," and throw\n",[18,1079,1080],{},"ConcurrentModificationException"," if the map is structurally modified outside the iterator\nduring iteration.",[86,1083,1085],{"className":88,"code":1084,"language":90,"meta":91,"style":91},"Map\u003CString,String> m = new HashMap\u003C>();\nm.put(null, \"x\");   \u002F\u002F legal — single null key\nm.put(\"a\", null);   \u002F\u002F legal — null value\nm.containsKey(\"a\"); \u002F\u002F true, even though the value is null\n\n\u002F\u002F safe structural mutation while iterating:\nIterator\u003CString> it = m.keySet().iterator();\nwhile (it.hasNext()) { if (it.next().startsWith(\"x\")) it.remove(); }\n",[18,1086,1087,1110,1131,1150,1166,1170,1175,1202],{"__ignoreMap":91},[95,1088,1089,1092,1095,1097,1099,1102,1104,1107],{"class":97,"line":98},[95,1090,1091],{"class":112},"Map\u003C",[95,1093,1094],{"class":101},"String",[95,1096,119],{"class":112},[95,1098,1094],{"class":101},[95,1100,1101],{"class":112},"> m ",[95,1103,300],{"class":101},[95,1105,1106],{"class":101}," new",[95,1108,1109],{"class":112}," HashMap\u003C>();\n",[95,1111,1112,1115,1117,1119,1121,1123,1126,1128],{"class":97,"line":128},[95,1113,1114],{"class":112},"m.",[95,1116,33],{"class":108},[95,1118,727],{"class":112},[95,1120,332],{"class":281},[95,1122,555],{"class":112},[95,1124,1125],{"class":730},"\"x\"",[95,1127,816],{"class":112},[95,1129,1130],{"class":140},"\u002F\u002F legal — single null key\n",[95,1132,1133,1135,1137,1139,1141,1143,1145,1147],{"class":97,"line":144},[95,1134,1114],{"class":112},[95,1136,33],{"class":108},[95,1138,727],{"class":112},[95,1140,731],{"class":730},[95,1142,555],{"class":112},[95,1144,332],{"class":281},[95,1146,816],{"class":112},[95,1148,1149],{"class":140},"\u002F\u002F legal — null value\n",[95,1151,1152,1154,1156,1158,1160,1163],{"class":97,"line":152},[95,1153,1114],{"class":112},[95,1155,1068],{"class":108},[95,1157,727],{"class":112},[95,1159,731],{"class":730},[95,1161,1162],{"class":112},"); ",[95,1164,1165],{"class":140},"\u002F\u002F true, even though the value is null\n",[95,1167,1168],{"class":97,"line":158},[95,1169,569],{"emptyLinePlaceholder":568},[95,1171,1172],{"class":97,"line":176},[95,1173,1174],{"class":140},"\u002F\u002F safe structural mutation while iterating:\n",[95,1176,1177,1180,1182,1185,1187,1190,1193,1196,1199],{"class":97,"line":608},[95,1178,1179],{"class":112},"Iterator\u003C",[95,1181,1094],{"class":101},[95,1183,1184],{"class":112},"> it ",[95,1186,300],{"class":101},[95,1188,1189],{"class":112}," m.",[95,1191,1192],{"class":108},"keySet",[95,1194,1195],{"class":112},"().",[95,1197,1198],{"class":108},"iterator",[95,1200,1201],{"class":112},"();\n",[95,1203,1205,1208,1211,1214,1217,1219,1221,1223,1225,1228,1230,1232,1235,1238],{"class":97,"line":1204},8,[95,1206,1207],{"class":101},"while",[95,1209,1210],{"class":112}," (it.",[95,1212,1213],{"class":108},"hasNext",[95,1215,1216],{"class":112},"()) { ",[95,1218,584],{"class":101},[95,1220,1210],{"class":112},[95,1222,83],{"class":108},[95,1224,1195],{"class":112},[95,1226,1227],{"class":108},"startsWith",[95,1229,727],{"class":112},[95,1231,1125],{"class":730},[95,1233,1234],{"class":112},")) it.",[95,1236,1237],{"class":108},"remove",[95,1239,1240],{"class":112},"(); }\n",[15,1242,1243,1244,1247,1248,1251,1252,848],{},"Fail-fast is a ",[23,1245,1246],{},"best-effort bug detector",", not a guarantee — never write logic that\ndepends on the exception firing. To mutate while iterating, use ",[18,1249,1250],{},"iterator.remove()",",\ncollect-then-remove, or ",[18,1253,1254],{},"removeIf",[10,1256,1258],{"id":1257},"thread-safety-and-concurrenthashmap","Thread safety and ConcurrentHashMap",[15,1260,1261,1263,1264,1266,1267,1270,1271,1273,1274,1276,1277,1280,1281,848],{},[18,1262,20],{}," has no synchronization, so concurrent writes can interleave and corrupt the\ntable, lose updates, or report a wrong ",[18,1265,517],{},". Pre-Java-8 JDKs were also vulnerable to an\ninfamous ",[23,1268,1269],{},"infinite loop on resize"," — two threads resizing at once could splice a chain\ninto a cycle, and a later ",[18,1272,29],{}," would spin at 100% CPU forever. Java 8's order-preserving\nsplit removed that specific cycle, but ",[18,1275,20],{}," is still ",[23,1278,1279],{},"not thread-safe",". For shared\nmaps, reach for ",[23,1282,1283],{},[18,1284,1285],{},"ConcurrentHashMap",[86,1287,1289],{"className":88,"code":1288,"language":90,"meta":91,"style":91},"ConcurrentHashMap\u003CString,Long> counts = new ConcurrentHashMap\u003C>();\ncounts.merge(\"hit\", 1L, Long::sum);   \u002F\u002F atomic increment, no external lock\n",[18,1290,1291,1313],{"__ignoreMap":91},[95,1292,1293,1296,1298,1300,1303,1306,1308,1310],{"class":97,"line":98},[95,1294,1295],{"class":112},"ConcurrentHashMap\u003C",[95,1297,1094],{"class":101},[95,1299,119],{"class":112},[95,1301,1302],{"class":101},"Long",[95,1304,1305],{"class":112},"> counts ",[95,1307,300],{"class":101},[95,1309,1106],{"class":101},[95,1311,1312],{"class":112}," ConcurrentHashMap\u003C>();\n",[95,1314,1315,1318,1321,1323,1326,1328,1331,1334,1337,1340],{"class":97,"line":128},[95,1316,1317],{"class":112},"counts.",[95,1319,1320],{"class":108},"merge",[95,1322,727],{"class":112},[95,1324,1325],{"class":730},"\"hit\"",[95,1327,555],{"class":112},[95,1329,1330],{"class":281},"1L",[95,1332,1333],{"class":112},", Long",[95,1335,1336],{"class":101},"::",[95,1338,1339],{"class":112},"sum);   ",[95,1341,1342],{"class":140},"\u002F\u002F atomic increment, no external lock\n",[15,1344,1345,1346,1348,1349,1352,1353,1355,1356,1359,1360,1363,1364,1367,1368,1371,1372,1375,1376,1378,1379,1382,1383,1385],{},"Modern ",[18,1347,1285],{}," uses the same ",[18,1350,1351],{},"Node[]"," table as ",[18,1354,20],{}," but adds ",[23,1357,1358],{},"per-bin\nlocking"," plus ",[23,1361,1362],{},"CAS",": an empty bin is filled with a lock-free compare-and-swap, while a\nnon-empty bin is updated under a ",[18,1365,1366],{},"synchronized"," block on its first node — so contention is\nlimited to the keys colliding in that one bin. Reads are non-blocking (",[18,1369,1370],{},"volatile"," fields),\nresizes are cooperative across threads, and its iterators are ",[23,1373,1374],{},"weakly consistent"," (they\nnever throw ",[18,1377,1080],{},"). This is why it scales far past the legacy\n",[18,1380,1381],{},"Hashtable",", which wraps every method in ",[18,1384,1366],{}," and serializes all access.",[10,1387,1389],{"id":1388},"hashmap-vs-linkedhashmap-vs-treemap","HashMap vs LinkedHashMap vs TreeMap",[15,1391,1392,1393,1396],{},"All three implement ",[18,1394,1395],{},"Map","; they differ in iteration order and complexity. Pick the cheapest\none that satisfies your ordering needs.",[1398,1399,1400,1418],"table",{},[1401,1402,1403],"thead",{},[1404,1405,1406,1409,1412,1415],"tr",{},[1407,1408,1395],"th",{},[1407,1410,1411],{},"Ordering",[1407,1413,1414],{},"get\u002Fput",[1407,1416,1417],{},"Backing structure",[1419,1420,1421,1437,1452],"tbody",{},[1404,1422,1423,1428,1431,1434],{},[1424,1425,1426],"td",{},[18,1427,20],{},[1424,1429,1430],{},"none (arbitrary)",[1424,1432,1433],{},"O(1) avg",[1424,1435,1436],{},"hash table",[1404,1438,1439,1444,1447,1449],{},[1424,1440,1441],{},[18,1442,1443],{},"LinkedHashMap",[1424,1445,1446],{},"insertion or access order",[1424,1448,1433],{},[1424,1450,1451],{},"hash table + linked list",[1404,1453,1454,1459,1462,1464],{},[1424,1455,1456],{},[18,1457,1458],{},"TreeMap",[1424,1460,1461],{},"sorted by key",[1424,1463,837],{},[1424,1465,648],{},[86,1467,1469],{"className":88,"code":1468,"language":90,"meta":91,"style":91},"new HashMap\u003C>();        \u002F\u002F fastest, no order guarantee\nnew LinkedHashMap\u003C>();  \u002F\u002F predictable iteration; access-order mode powers LRU caches\nnew TreeMap\u003C>();        \u002F\u002F keys come out sorted; needs Comparable or a Comparator\n",[18,1470,1471,1481,1491],{"__ignoreMap":91},[95,1472,1473,1475,1478],{"class":97,"line":98},[95,1474,452],{"class":101},[95,1476,1477],{"class":112}," HashMap\u003C>();        ",[95,1479,1480],{"class":140},"\u002F\u002F fastest, no order guarantee\n",[95,1482,1483,1485,1488],{"class":97,"line":128},[95,1484,452],{"class":101},[95,1486,1487],{"class":112}," LinkedHashMap\u003C>();  ",[95,1489,1490],{"class":140},"\u002F\u002F predictable iteration; access-order mode powers LRU caches\n",[95,1492,1493,1495,1498],{"class":97,"line":144},[95,1494,452],{"class":101},[95,1496,1497],{"class":112}," TreeMap\u003C>();        ",[95,1499,1500],{"class":140},"\u002F\u002F keys come out sorted; needs Comparable or a Comparator\n",[15,1502,1503,1504,1506,1507,1509,1510,1513,1514,1517,1518,1520,1521,555,1524,1527,1528,1531,1532,555,1535,1527,1538,1541],{},"Default to ",[18,1505,20],{},". Choose ",[18,1508,1443],{}," when you need stable iteration order or an\n",[23,1511,1512],{},"LRU cache"," (access-order mode plus ",[18,1515,1516],{},"removeEldestEntry","), and ",[18,1519,1458],{}," when you need\nsorted keys or range queries like ",[18,1522,1523],{},"firstKey",[18,1525,1526],{},"headMap",", and ",[18,1529,1530],{},"subMap",". The same pattern\nextends to sets: ",[18,1533,1534],{},"HashSet",[18,1536,1537],{},"LinkedHashSet",[18,1539,1540],{},"TreeSet"," are thin wrappers over their\nmatching maps.",[10,1543,1545],{"id":1544},"recap","Recap",[15,1547,1548,1550,1551,1554,1555,1557,1558,1562,1563,1565,1566,1569,1570,1573,1574,1577,1578,1580,1581,30,1588,1591,1592,1594,1595,1597,1598,1600,1601,1605,1606,1608,1609,1611],{},[18,1549,20],{}," stores entries in a power-of-two ",[23,1552,1553],{},"bucket array"," of ",[18,1556,66],{},"s, indexes them with\nthe bit trick ",[23,1559,1560],{},[18,1561,484],{}," after a cheap ",[23,1564,229],{}," function mixes the high bits\ndown, and keeps chains short by ",[23,1567,1568],{},"resizing"," at ",[18,1571,1572],{},"capacity × 0.75",". Collisions chain into\nlinked lists that ",[23,1575,1576],{},"treeify"," into red-black trees past 8 entries (with a table of 64+),\nbounding the worst case at ",[23,1579,837],{},". The whole thing rests on a sound\n",[23,1582,1583,860,1585,1587],{},[18,1584,222],{},[18,1586,189],{}," contract",[23,1589,1590],{},"immutable keys"," — break either and lookups silently\nfail or degrade to ",[23,1593,627],{},". ",[18,1596,20],{}," allows one null key and many null values but is\n",[23,1599,1279],{},"; use ",[23,1602,1603],{},[18,1604,1285],{}," with its per-bin locking and CAS for shared\naccess, and pick ",[18,1607,1443],{}," or ",[18,1610,1458],{}," when you need ordering. Understand these\nmoving parts and you can answer almost any HashMap question from first principles.",[1613,1614,1615],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":91,"searchDepth":128,"depth":128,"links":1617},[1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630],{"id":12,"depth":128,"text":13},{"id":48,"depth":128,"text":49},{"id":200,"depth":128,"text":201},{"id":340,"depth":128,"text":341},{"id":432,"depth":128,"text":433},{"id":488,"depth":128,"text":489},{"id":631,"depth":128,"text":632},{"id":690,"depth":128,"text":691},{"id":851,"depth":128,"text":852},{"id":1040,"depth":128,"text":1041},{"id":1257,"depth":128,"text":1258},{"id":1388,"depth":128,"text":1389},{"id":1544,"depth":128,"text":1545},"A deep dive into Java HashMap internals — the bucket array and Node, the spread function, power-of-two indexing, load factor and resizing, collision handling and treeification, the hashCode\u002Fequals contract, null handling, and thread safety.","hard","md","Java",{},"\u002Fblog\u002Fjava-hashmap-internals","\u002Fjava\u002Fcollections\u002Fhashmap-internals",{"title":5,"description":1631},"blog\u002Fjava-hashmap-internals","HashMap Internals","Collections","collections","2026-06-20","rtFXQMHAWWcyROf_x8ikwOecdIW_aeSmsKwSSu8AG4w",1782244089544]