[{"data":1,"prerenderedAt":1931},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-set-hashset-treeset-linkedhashset":3},{"id":4,"title":5,"body":6,"description":1917,"difficulty":1918,"extension":1919,"framework":1920,"frameworkSlug":50,"meta":1921,"navigation":248,"order":105,"path":1922,"qaPath":1923,"seo":1924,"stem":1925,"subtopic":1926,"topic":1927,"topicSlug":1928,"updated":1929,"__hash__":1930},"blog\u002Fblog\u002Fjava-set-hashset-treeset-linkedhashset.md","Java Set Explained — HashSet vs LinkedHashSet vs TreeSet",{"type":7,"value":8,"toc":1903},"minimark",[9,14,45,133,143,147,196,292,315,319,337,444,469,473,494,579,589,593,623,762,808,812,849,995,1011,1015,1018,1149,1184,1193,1197,1222,1361,1370,1374,1401,1511,1517,1521,1546,1695,1706,1710,1731,1819,1829,1833,1899],[10,11,13],"h2",{"id":12},"what-a-set-actually-promises","What a Set actually promises",[15,16,17,18,22,23,26,27,31,32,35,36,39,40,44],"p",{},"A ",[19,20,21],"code",{},"Set"," is a ",[19,24,25],{},"Collection"," with exactly one extra rule: ",[28,29,30],"strong",{},"no duplicate elements",". There's\nno index and no positional access — the operation a set is built to make fast is\n",[28,33,34],{},"membership"," (",[19,37,38],{},"contains","). Everything interesting about the three standard\nimplementations flows from a single question: ",[41,42,43],"em",{},"how does this set decide two elements are\nthe same?"," Get that straight and the rest — ordering, null handling, complexity — falls\nout naturally.",[46,47,52],"pre",{"className":48,"code":49,"language":50,"meta":51,"style":51},"language-java shiki shiki-themes github-light github-dark","Set\u003CString> tags = new HashSet\u003C>();\ntags.add(\"java\");      \u002F\u002F true  — new element\ntags.add(\"java\");      \u002F\u002F false — duplicate, silently ignored\ntags.size();           \u002F\u002F 1\n","java","",[19,53,54,79,103,119],{"__ignoreMap":51},[55,56,59,63,67,70,73,76],"span",{"class":57,"line":58},"line",1,[55,60,62],{"class":61},"sVt8B","Set\u003C",[55,64,66],{"class":65},"szBVR","String",[55,68,69],{"class":61},"> tags ",[55,71,72],{"class":65},"=",[55,74,75],{"class":65}," new",[55,77,78],{"class":61}," HashSet\u003C>();\n",[55,80,82,85,89,92,96,99],{"class":57,"line":81},2,[55,83,84],{"class":61},"tags.",[55,86,88],{"class":87},"sScJk","add",[55,90,91],{"class":61},"(",[55,93,95],{"class":94},"sZZnC","\"java\"",[55,97,98],{"class":61},");      ",[55,100,102],{"class":101},"sJ8bj","\u002F\u002F true  — new element\n",[55,104,106,108,110,112,114,116],{"class":57,"line":105},3,[55,107,84],{"class":61},[55,109,88],{"class":87},[55,111,91],{"class":61},[55,113,95],{"class":94},[55,115,98],{"class":61},[55,117,118],{"class":101},"\u002F\u002F false — duplicate, silently ignored\n",[55,120,122,124,127,130],{"class":57,"line":121},4,[55,123,84],{"class":61},[55,125,126],{"class":87},"size",[55,128,129],{"class":61},"();           ",[55,131,132],{"class":101},"\u002F\u002F 1\n",[15,134,135,136,138,139,142],{},"The payoff is performance: a ",[19,137,21],{}," answers \"is it in there?\" far faster than a ",[19,140,141],{},"List",",\nwhich has to scan element by element.",[10,144,146],{"id":145},"dedup-is-defined-by-equalshashcode-or-compareto","Dedup is defined by equals\u002FhashCode — or compareTo",[15,148,149,150,153,154,157,158,161,162,165,166,171,172,177,178,181,182,187,188,191,192,195],{},"\"Duplicate\" is ",[28,151,152],{},"not"," reference identity; it's logical equality, and ",[41,155,156],{},"which"," equality\ndepends on the implementation. Hash-based sets (",[19,159,160],{},"HashSet",", ",[19,163,164],{},"LinkedHashSet",") use\n",[28,167,168],{},[19,169,170],{},"hashCode()"," to pick a bucket and ",[28,173,174],{},[19,175,176],{},"equals()"," to compare within it. A ",[19,179,180],{},"TreeSet","\nignores both and uses ",[28,183,184],{},[19,185,186],{},"compareTo()"," (or a supplied ",[19,189,190],{},"Comparator","): two elements are\n\"equal\" precisely when comparison returns ",[19,193,194],{},"0",".",[46,197,199],{"className":48,"code":198,"language":50,"meta":51,"style":51},"Set\u003CString> hash = new HashSet\u003C>();\nhash.add(\"Java\"); hash.add(\"JAVA\");   \u002F\u002F equals: different -> size 2\n\nSet\u003CString> tree = new TreeSet\u003C>(String.CASE_INSENSITIVE_ORDER);\ntree.add(\"Java\"); tree.add(\"JAVA\");   \u002F\u002F compare == 0 -> size 1\n",[19,200,201,216,244,250,266],{"__ignoreMap":51},[55,202,203,205,207,210,212,214],{"class":57,"line":58},[55,204,62],{"class":61},[55,206,66],{"class":65},[55,208,209],{"class":61},"> hash ",[55,211,72],{"class":65},[55,213,75],{"class":65},[55,215,78],{"class":61},[55,217,218,221,223,225,228,231,233,235,238,241],{"class":57,"line":81},[55,219,220],{"class":61},"hash.",[55,222,88],{"class":87},[55,224,91],{"class":61},[55,226,227],{"class":94},"\"Java\"",[55,229,230],{"class":61},"); hash.",[55,232,88],{"class":87},[55,234,91],{"class":61},[55,236,237],{"class":94},"\"JAVA\"",[55,239,240],{"class":61},");   ",[55,242,243],{"class":101},"\u002F\u002F equals: different -> size 2\n",[55,245,246],{"class":57,"line":105},[55,247,249],{"emptyLinePlaceholder":248},true,"\n",[55,251,252,254,256,259,261,263],{"class":57,"line":121},[55,253,62],{"class":61},[55,255,66],{"class":65},[55,257,258],{"class":61},"> tree ",[55,260,72],{"class":65},[55,262,75],{"class":65},[55,264,265],{"class":61}," TreeSet\u003C>(String.CASE_INSENSITIVE_ORDER);\n",[55,267,269,272,274,276,278,281,283,285,287,289],{"class":57,"line":268},5,[55,270,271],{"class":61},"tree.",[55,273,88],{"class":87},[55,275,91],{"class":61},[55,277,227],{"class":94},[55,279,280],{"class":61},"); tree.",[55,282,88],{"class":87},[55,284,91],{"class":61},[55,286,237],{"class":94},[55,288,240],{"class":61},[55,290,291],{"class":101},"\u002F\u002F compare == 0 -> size 1\n",[15,293,294,295,297,298,301,302,311,312,314],{},"This split is the most common interview trap. A ",[19,296,180],{}," can drop an element as a\n\"duplicate\" even though ",[19,299,300],{},"equals"," says it differs, so the rule is: ",[28,303,304,305,308,309],{},"keep ",[19,306,307],{},"compareTo","\nconsistent with ",[19,310,300],{},". Otherwise the same objects behave differently depending on\nwhich ",[19,313,21],{}," you happen to use.",[10,316,318],{"id":317},"hashset-a-hashmap-wearing-a-disguise","HashSet — a HashMap wearing a disguise",[15,320,321,323,324,329,330,333,334,336],{},[19,322,160],{}," is a thin wrapper around a ",[28,325,326],{},[19,327,328],{},"HashMap",". Each element is stored as a ",[41,331,332],{},"key",", and\nevery key maps to the same shared dummy value. That's the whole trick — so everything you\nknow about ",[19,335,328],{}," (buckets, load factor, bins that treeify under collision pressure)\napplies unchanged.",[46,338,340],{"className":48,"code":339,"language":50,"meta":51,"style":51},"\u002F\u002F Conceptually, inside HashSet:\nprivate transient HashMap\u003CE, Object> map;\nprivate static final Object PRESENT = new Object();\n\npublic boolean add(E e) {\n    return map.put(e, PRESENT) == null;  \u002F\u002F true only if the key was new\n}\n",[19,341,342,347,369,392,396,410,438],{"__ignoreMap":51},[55,343,344],{"class":57,"line":58},[55,345,346],{"class":101},"\u002F\u002F Conceptually, inside HashSet:\n",[55,348,349,352,355,358,361,363,366],{"class":57,"line":81},[55,350,351],{"class":65},"private",[55,353,354],{"class":65}," transient",[55,356,357],{"class":61}," HashMap\u003C",[55,359,360],{"class":65},"E",[55,362,161],{"class":61},[55,364,365],{"class":65},"Object",[55,367,368],{"class":61},"> map;\n",[55,370,371,373,376,379,382,384,386,389],{"class":57,"line":105},[55,372,351],{"class":65},[55,374,375],{"class":65}," static",[55,377,378],{"class":65}," final",[55,380,381],{"class":61}," Object PRESENT ",[55,383,72],{"class":65},[55,385,75],{"class":65},[55,387,388],{"class":87}," Object",[55,390,391],{"class":61},"();\n",[55,393,394],{"class":57,"line":121},[55,395,249],{"emptyLinePlaceholder":248},[55,397,398,401,404,407],{"class":57,"line":268},[55,399,400],{"class":65},"public",[55,402,403],{"class":65}," boolean",[55,405,406],{"class":87}," add",[55,408,409],{"class":61},"(E e) {\n",[55,411,413,416,419,422,425,428,432,435],{"class":57,"line":412},6,[55,414,415],{"class":65},"    return",[55,417,418],{"class":61}," map.",[55,420,421],{"class":87},"put",[55,423,424],{"class":61},"(e, PRESENT) ",[55,426,427],{"class":65},"==",[55,429,431],{"class":430},"sj4cs"," null",[55,433,434],{"class":61},";  ",[55,436,437],{"class":101},"\u002F\u002F true only if the key was new\n",[55,439,441],{"class":57,"line":440},7,[55,442,443],{"class":61},"}\n",[15,445,446,447,450,451,453,454,453,456,161,459,462,463,453,466,468],{},"The consequences: ",[28,448,449],{},"average O(1)"," ",[19,452,88],{},"\u002F",[19,455,38],{},[19,457,458],{},"remove",[28,460,461],{},"no ordering guarantee",",\nand total dependence on a correct ",[19,464,465],{},"hashCode",[19,467,300],{},". If hashes collide badly,\nperformance slides toward O(n) — or O(log n) once an over-full bucket converts to a tree.\nThis is your default set: reach for anything else only when you need something it can't\ngive you.",[10,470,472],{"id":471},"linkedhashset-dedup-that-remembers-insertion-order","LinkedHashSet — dedup that remembers insertion order",[15,474,475,477,478,480,481,486,487,489,490,493],{},[19,476,164],{}," extends ",[19,479,160],{}," but is backed by a ",[28,482,483],{},[19,484,485],{},"LinkedHashMap",", which threads a\ndoubly-linked list through the entries. You keep ",[19,488,160],{},"'s O(1) operations, but\niteration now follows ",[28,491,492],{},"insertion order"," — deterministic and reproducible.",[46,495,497],{"className":48,"code":496,"language":50,"meta":51,"style":51},"Set\u003CString> hs = new LinkedHashSet\u003C>();\nhs.add(\"c\"); hs.add(\"a\"); hs.add(\"b\");\nhs.add(\"a\");                        \u002F\u002F duplicate ignored, order untouched\nSystem.out.println(hs);            \u002F\u002F [c, a, b] — exactly as inserted\n",[19,498,499,515,549,565],{"__ignoreMap":51},[55,500,501,503,505,508,510,512],{"class":57,"line":58},[55,502,62],{"class":61},[55,504,66],{"class":65},[55,506,507],{"class":61},"> hs ",[55,509,72],{"class":65},[55,511,75],{"class":65},[55,513,514],{"class":61}," LinkedHashSet\u003C>();\n",[55,516,517,520,522,524,527,530,532,534,537,539,541,543,546],{"class":57,"line":81},[55,518,519],{"class":61},"hs.",[55,521,88],{"class":87},[55,523,91],{"class":61},[55,525,526],{"class":94},"\"c\"",[55,528,529],{"class":61},"); hs.",[55,531,88],{"class":87},[55,533,91],{"class":61},[55,535,536],{"class":94},"\"a\"",[55,538,529],{"class":61},[55,540,88],{"class":87},[55,542,91],{"class":61},[55,544,545],{"class":94},"\"b\"",[55,547,548],{"class":61},");\n",[55,550,551,553,555,557,559,562],{"class":57,"line":105},[55,552,519],{"class":61},[55,554,88],{"class":87},[55,556,91],{"class":61},[55,558,536],{"class":94},[55,560,561],{"class":61},");                        ",[55,563,564],{"class":101},"\u002F\u002F duplicate ignored, order untouched\n",[55,566,567,570,573,576],{"class":57,"line":121},[55,568,569],{"class":61},"System.out.",[55,571,572],{"class":87},"println",[55,574,575],{"class":61},"(hs);            ",[55,577,578],{"class":101},"\u002F\u002F [c, a, b] — exactly as inserted\n",[15,580,581,582,585,586,588],{},"The only cost is a slightly larger footprint (the prev\u002Fnext links per entry). Use it\nwhenever you need ",[28,583,584],{},"dedup without scrambling order"," — the textbook case is stripping\nduplicates from a list while preserving the original sequence, something plain ",[19,587,160],{},"\nwould quietly mangle.",[10,590,592],{"id":591},"treeset-sorted-red-black-and-navigable","TreeSet — sorted, red-black, and navigable",[15,594,595,597,598,603,604,607,608,611,612,615,616,618,619,622],{},[19,596,180],{}," is backed by a ",[28,599,600],{},[19,601,602],{},"TreeMap",", a ",[28,605,606],{},"red-black tree"," (a self-balancing binary\nsearch tree). Elements stay in ",[28,609,610],{},"sorted order"," — natural ordering via ",[19,613,614],{},"Comparable",", or a\n",[19,617,190],{}," you pass to the constructor — and core operations cost ",[28,620,621],{},"O(log n)"," instead\nof O(1). In exchange you get range and neighbour queries for free, because the data is\nalready ordered.",[46,624,626],{"className":48,"code":625,"language":50,"meta":51,"style":51},"NavigableSet\u003CInteger> s = new TreeSet\u003C>(List.of(10, 20, 30, 40));\ns.first();          \u002F\u002F 10        smallest\ns.last();           \u002F\u002F 40        largest\ns.floor(25);        \u002F\u002F 20        greatest \u003C= 25\ns.ceiling(25);      \u002F\u002F 30        smallest >= 25\ns.higher(20);       \u002F\u002F 30        strictly > 20\ns.descendingSet();  \u002F\u002F [40, 30, 20, 10]  reverse view, no copy\n",[19,627,628,672,686,698,716,732,749],{"__ignoreMap":51},[55,629,630,633,636,639,641,643,646,649,651,654,656,659,661,664,666,669],{"class":57,"line":58},[55,631,632],{"class":61},"NavigableSet\u003C",[55,634,635],{"class":65},"Integer",[55,637,638],{"class":61},"> s ",[55,640,72],{"class":65},[55,642,75],{"class":65},[55,644,645],{"class":61}," TreeSet\u003C>(List.",[55,647,648],{"class":87},"of",[55,650,91],{"class":61},[55,652,653],{"class":430},"10",[55,655,161],{"class":61},[55,657,658],{"class":430},"20",[55,660,161],{"class":61},[55,662,663],{"class":430},"30",[55,665,161],{"class":61},[55,667,668],{"class":430},"40",[55,670,671],{"class":61},"));\n",[55,673,674,677,680,683],{"class":57,"line":81},[55,675,676],{"class":61},"s.",[55,678,679],{"class":87},"first",[55,681,682],{"class":61},"();          ",[55,684,685],{"class":101},"\u002F\u002F 10        smallest\n",[55,687,688,690,693,695],{"class":57,"line":105},[55,689,676],{"class":61},[55,691,692],{"class":87},"last",[55,694,129],{"class":61},[55,696,697],{"class":101},"\u002F\u002F 40        largest\n",[55,699,700,702,705,707,710,713],{"class":57,"line":121},[55,701,676],{"class":61},[55,703,704],{"class":87},"floor",[55,706,91],{"class":61},[55,708,709],{"class":430},"25",[55,711,712],{"class":61},");        ",[55,714,715],{"class":101},"\u002F\u002F 20        greatest \u003C= 25\n",[55,717,718,720,723,725,727,729],{"class":57,"line":268},[55,719,676],{"class":61},[55,721,722],{"class":87},"ceiling",[55,724,91],{"class":61},[55,726,709],{"class":430},[55,728,98],{"class":61},[55,730,731],{"class":101},"\u002F\u002F 30        smallest >= 25\n",[55,733,734,736,739,741,743,746],{"class":57,"line":412},[55,735,676],{"class":61},[55,737,738],{"class":87},"higher",[55,740,91],{"class":61},[55,742,658],{"class":430},[55,744,745],{"class":61},");       ",[55,747,748],{"class":101},"\u002F\u002F 30        strictly > 20\n",[55,750,751,753,756,759],{"class":57,"line":440},[55,752,676],{"class":61},[55,754,755],{"class":87},"descendingSet",[55,757,758],{"class":61},"();  ",[55,760,761],{"class":101},"\u002F\u002F [40, 30, 20, 10]  reverse view, no copy\n",[15,763,764,766,767,772,773,776,777,453,780,453,783,786,787,790,791,793,794,796,797,453,799,453,801,803,804,807],{},[19,765,180],{}," implements ",[28,768,769],{},[19,770,771],{},"NavigableSet"," (which extends ",[19,774,775],{},"SortedSet","), so you also get live\n",[19,778,779],{},"headSet",[19,781,782],{},"tailSet",[19,784,785],{},"subSet"," range ",[41,788,789],{},"views"," backed by the original set. Declare your\nvariable as ",[19,792,771],{}," rather than ",[19,795,775],{}," to keep ",[19,798,704],{},[19,800,722],{},[19,802,738],{},"\u002F\n",[19,805,806],{},"lower"," in reach — they're the whole reason to prefer a tree over a hash set for\n\"nearest neighbour\" problems.",[10,809,811],{"id":810},"the-treeset-null-and-comparable-gotcha","The TreeSet null and Comparable gotcha",[15,813,17,814,816,817,820,821,824,825,828,829,832,833,836,837,839,840,842,843,846,847,195],{},[19,815,180],{}," must compare every element it stores, and that creates two failure modes\npeople hit at runtime, not compile time. First, ",[28,818,819],{},"null",": with natural ordering,\n",[19,822,823],{},"add(null)"," throws ",[19,826,827],{},"NullPointerException"," because the set tries to call ",[19,830,831],{},"null.compareTo",".\nSecond, ",[28,834,835],{},"non-comparable elements",": a type with no ",[19,838,614],{}," and no ",[19,841,190],{}," blows\nup with ",[19,844,845],{},"ClassCastException"," on the first real ",[19,848,88],{},[46,850,852],{"className":48,"code":851,"language":50,"meta":51,"style":51},"Set\u003CString> tree = new TreeSet\u003C>();\ntree.add(null);                    \u002F\u002F NullPointerException (HashSet would allow one null)\n\nclass Point { int x, y; }\nSet\u003CPoint> bad = new TreeSet\u003C>();\nbad.add(new Point());              \u002F\u002F ClassCastException — Point isn't Comparable\n\nSet\u003CPoint> ok = new TreeSet\u003C>(Comparator.comparingInt(p -> p.x));\nok.add(new Point());               \u002F\u002F fine — an ordering was supplied\n",[19,853,854,869,885,889,906,922,942,946,975],{"__ignoreMap":51},[55,855,856,858,860,862,864,866],{"class":57,"line":58},[55,857,62],{"class":61},[55,859,66],{"class":65},[55,861,258],{"class":61},[55,863,72],{"class":65},[55,865,75],{"class":65},[55,867,868],{"class":61}," TreeSet\u003C>();\n",[55,870,871,873,875,877,879,882],{"class":57,"line":81},[55,872,271],{"class":61},[55,874,88],{"class":87},[55,876,91],{"class":61},[55,878,819],{"class":430},[55,880,881],{"class":61},");                    ",[55,883,884],{"class":101},"\u002F\u002F NullPointerException (HashSet would allow one null)\n",[55,886,887],{"class":57,"line":105},[55,888,249],{"emptyLinePlaceholder":248},[55,890,891,894,897,900,903],{"class":57,"line":121},[55,892,893],{"class":65},"class",[55,895,896],{"class":87}," Point",[55,898,899],{"class":61}," { ",[55,901,902],{"class":65},"int",[55,904,905],{"class":61}," x, y; }\n",[55,907,908,910,913,916,918,920],{"class":57,"line":268},[55,909,62],{"class":61},[55,911,912],{"class":65},"Point",[55,914,915],{"class":61},"> bad ",[55,917,72],{"class":65},[55,919,75],{"class":65},[55,921,868],{"class":61},[55,923,924,927,929,931,934,936,939],{"class":57,"line":412},[55,925,926],{"class":61},"bad.",[55,928,88],{"class":87},[55,930,91],{"class":61},[55,932,933],{"class":65},"new",[55,935,896],{"class":87},[55,937,938],{"class":61},"());              ",[55,940,941],{"class":101},"\u002F\u002F ClassCastException — Point isn't Comparable\n",[55,943,944],{"class":57,"line":440},[55,945,249],{"emptyLinePlaceholder":248},[55,947,949,951,953,956,958,960,963,966,969,972],{"class":57,"line":948},8,[55,950,62],{"class":61},[55,952,912],{"class":65},[55,954,955],{"class":61},"> ok ",[55,957,72],{"class":65},[55,959,75],{"class":65},[55,961,962],{"class":61}," TreeSet\u003C>(Comparator.",[55,964,965],{"class":87},"comparingInt",[55,967,968],{"class":61},"(p ",[55,970,971],{"class":65},"->",[55,973,974],{"class":61}," p.x));\n",[55,976,978,981,983,985,987,989,992],{"class":57,"line":977},9,[55,979,980],{"class":61},"ok.",[55,982,88],{"class":87},[55,984,91],{"class":61},[55,986,933],{"class":65},[55,988,896],{"class":87},[55,990,991],{"class":61},"());               ",[55,993,994],{"class":101},"\u002F\u002F fine — an ordering was supplied\n",[15,996,997,998,450,1001,1003,1004,1006,1007,1010],{},"Note both failures are deferred: an ",[41,999,1000],{},"empty",[19,1002,180],{}," is perfectly happy. It only throws\nwhen it must compare. Treat ",[19,1005,180],{}," as ",[28,1008,1009],{},"null-hostile"," and always give it an ordering\nfor custom types.",[10,1012,1014],{"id":1013},"choosing-between-the-three","Choosing between the three",[15,1016,1017],{},"Here's the side-by-side every interviewer wants to see:",[1019,1020,1021,1043],"table",{},[1022,1023,1024],"thead",{},[1025,1026,1027,1031,1035,1039],"tr",{},[1028,1029,1030],"th",{},"Feature",[1028,1032,1033],{},[19,1034,160],{},[1028,1036,1037],{},[19,1038,164],{},[1028,1040,1041],{},[19,1042,180],{},[1044,1045,1046,1065,1078,1095,1113,1137],"tbody",{},[1025,1047,1048,1052,1056,1060],{},[1049,1050,1051],"td",{},"Backed by",[1049,1053,1054],{},[19,1055,328],{},[1049,1057,1058],{},[19,1059,485],{},[1049,1061,1062,1064],{},[19,1063,602],{}," (red-black tree)",[1025,1066,1067,1070,1073,1075],{},[1049,1068,1069],{},"Iteration order",[1049,1071,1072],{},"none",[1049,1074,492],{},[1049,1076,1077],{},"sorted",[1025,1079,1080,1088,1091,1093],{},[1049,1081,1082,453,1084,453,1086],{},[19,1083,88],{},[19,1085,38],{},[19,1087,458],{},[1049,1089,1090],{},"O(1) avg",[1049,1092,1090],{},[1049,1094,621],{},[1025,1096,1097,1102,1105,1107],{},[1049,1098,1099,1101],{},[19,1100,819],{}," allowed",[1049,1103,1104],{},"one",[1049,1106,1104],{},[1049,1108,1109,1112],{},[28,1110,1111],{},"no"," (natural order)",[1025,1114,1115,1118,1125,1131],{},[1049,1116,1117],{},"Equality test",[1049,1119,1120,1122,1123],{},[19,1121,300],{},"+",[19,1124,465],{},[1049,1126,1127,1122,1129],{},[19,1128,300],{},[19,1130,465],{},[1049,1132,1133,453,1135],{},[19,1134,307],{},[19,1136,190],{},[1025,1138,1139,1142,1144,1146],{},[1049,1140,1141],{},"Range\u002Fnavigation",[1049,1143,1111],{},[1049,1145,1111],{},[1049,1147,1148],{},"yes",[46,1150,1152],{"className":48,"code":1151,"language":50,"meta":51,"style":51},"new HashSet\u003C>();        \u002F\u002F fastest, order irrelevant — the default\nnew LinkedHashSet\u003C>();  \u002F\u002F dedup but keep insertion order\nnew TreeSet\u003C>();        \u002F\u002F need sorted iteration or range queries\n",[19,1153,1154,1164,1174],{"__ignoreMap":51},[55,1155,1156,1158,1161],{"class":57,"line":58},[55,1157,933],{"class":65},[55,1159,1160],{"class":61}," HashSet\u003C>();        ",[55,1162,1163],{"class":101},"\u002F\u002F fastest, order irrelevant — the default\n",[55,1165,1166,1168,1171],{"class":57,"line":81},[55,1167,933],{"class":65},[55,1169,1170],{"class":61}," LinkedHashSet\u003C>();  ",[55,1172,1173],{"class":101},"\u002F\u002F dedup but keep insertion order\n",[55,1175,1176,1178,1181],{"class":57,"line":105},[55,1177,933],{"class":65},[55,1179,1180],{"class":61}," TreeSet\u003C>();        ",[55,1182,1183],{"class":101},"\u002F\u002F need sorted iteration or range queries\n",[15,1185,1186,1187,1192],{},"None of the three is thread-safe. ",[28,1188,1189,1190],{},"Default to ",[19,1191,160],{}," and upgrade only when you\nspecifically need predictable order, sorting, or navigation.",[10,1194,1196],{"id":1195},"enumset-the-bit-vector-specialist","EnumSet — the bit-vector specialist",[15,1198,1199,1200,1203,1204,1207,1208,1211,1212,1215,1216,1218,1219,195],{},"When your elements are ",[28,1201,1202],{},"enum constants",", none of the above is the right answer:\n",[19,1205,1206],{},"EnumSet"," is. Internally it's a ",[28,1209,1210],{},"bit vector"," — usually a single ",[19,1213,1214],{},"long"," where each bit\nrepresents one constant — so operations are near-instant and memory is tiny. You build it\nwith factory methods, not ",[19,1217,933],{},", and iteration follows the enum's ",[28,1220,1221],{},"declaration order",[46,1223,1225],{"className":48,"code":1224,"language":50,"meta":51,"style":51},"enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }\n\nEnumSet\u003CDay> work    = EnumSet.range(Day.MON, Day.FRI);   \u002F\u002F [MON..FRI]\nEnumSet\u003CDay> weekend = EnumSet.complementOf(work);        \u002F\u002F [SAT, SUN]\nEnumSet\u003CDay> none    = EnumSet.noneOf(Day.class);\nEnumSet\u003CDay> all     = EnumSet.allOf(Day.class);\n",[19,1226,1227,1273,1277,1302,1324,1343],{"__ignoreMap":51},[55,1228,1229,1232,1235,1237,1240,1242,1245,1247,1250,1252,1255,1257,1260,1262,1265,1267,1270],{"class":57,"line":58},[55,1230,1231],{"class":65},"enum",[55,1233,1234],{"class":87}," Day",[55,1236,899],{"class":61},[55,1238,1239],{"class":430},"MON",[55,1241,161],{"class":61},[55,1243,1244],{"class":430},"TUE",[55,1246,161],{"class":61},[55,1248,1249],{"class":430},"WED",[55,1251,161],{"class":61},[55,1253,1254],{"class":430},"THU",[55,1256,161],{"class":61},[55,1258,1259],{"class":430},"FRI",[55,1261,161],{"class":61},[55,1263,1264],{"class":430},"SAT",[55,1266,161],{"class":61},[55,1268,1269],{"class":430},"SUN",[55,1271,1272],{"class":61}," }\n",[55,1274,1275],{"class":57,"line":81},[55,1276,249],{"emptyLinePlaceholder":248},[55,1278,1279,1282,1285,1288,1290,1293,1296,1299],{"class":57,"line":105},[55,1280,1281],{"class":61},"EnumSet\u003C",[55,1283,1284],{"class":65},"Day",[55,1286,1287],{"class":61},"> work    ",[55,1289,72],{"class":65},[55,1291,1292],{"class":61}," EnumSet.",[55,1294,1295],{"class":87},"range",[55,1297,1298],{"class":61},"(Day.MON, Day.FRI);   ",[55,1300,1301],{"class":101},"\u002F\u002F [MON..FRI]\n",[55,1303,1304,1306,1308,1311,1313,1315,1318,1321],{"class":57,"line":121},[55,1305,1281],{"class":61},[55,1307,1284],{"class":65},[55,1309,1310],{"class":61},"> weekend ",[55,1312,72],{"class":65},[55,1314,1292],{"class":61},[55,1316,1317],{"class":87},"complementOf",[55,1319,1320],{"class":61},"(work);        ",[55,1322,1323],{"class":101},"\u002F\u002F [SAT, SUN]\n",[55,1325,1326,1328,1330,1333,1335,1337,1340],{"class":57,"line":268},[55,1327,1281],{"class":61},[55,1329,1284],{"class":65},[55,1331,1332],{"class":61},"> none    ",[55,1334,72],{"class":65},[55,1336,1292],{"class":61},[55,1338,1339],{"class":87},"noneOf",[55,1341,1342],{"class":61},"(Day.class);\n",[55,1344,1345,1347,1349,1352,1354,1356,1359],{"class":57,"line":412},[55,1346,1281],{"class":61},[55,1348,1284],{"class":65},[55,1350,1351],{"class":61},"> all     ",[55,1353,72],{"class":65},[55,1355,1292],{"class":61},[55,1357,1358],{"class":87},"allOf",[55,1360,1342],{"class":61},[15,1362,1363,1364,1366,1367,1369],{},"For enum keys, ",[19,1365,1206],{}," beats ",[19,1368,160],{}," on every axis — speed, memory, and readability —\nso make it your reflex whenever the element type is an enum.",[10,1371,1373],{"id":1372},"thread-safe-and-concurrent-sets","Thread-safe and concurrent sets",[15,1375,1376,1377,1380,1381,1384,1385,1388,1389,1392,1393,1396,1397,1400],{},"The standard implementations are ",[28,1378,1379],{},"unsynchronized",". There are three escalating options.\n",[19,1382,1383],{},"Collections.synchronizedSet(...)"," wraps every method in a lock but still requires you to\n",[28,1386,1387],{},"manually synchronize during iteration",". ",[19,1390,1391],{},"ConcurrentHashMap.newKeySet()"," gives a true\nconcurrent hash set with lock-free reads and weakly consistent iteration — the right\ndefault for concurrent code. ",[19,1394,1395],{},"CopyOnWriteArraySet"," copies its whole backing array on every\nwrite, so it's perfect for ",[28,1398,1399],{},"small, read-mostly"," sets (think listener registries) and\nterrible for write-heavy ones.",[46,1402,1404],{"className":48,"code":1403,"language":50,"meta":51,"style":51},"Set\u003CString> sync = Collections.synchronizedSet(new HashSet\u003C>());\nsynchronized (sync) {                       \u002F\u002F iteration STILL needs the lock\n    for (String x : sync) { \u002F* ... *\u002F }\n}\n\nSet\u003CString> concurrent = ConcurrentHashMap.newKeySet();   \u002F\u002F preferred default\nSet\u003CListener> listeners = new CopyOnWriteArraySet\u003C>();    \u002F\u002F read-mostly, tiny\n",[19,1405,1406,1430,1441,1460,1464,1468,1491],{"__ignoreMap":51},[55,1407,1408,1410,1412,1415,1417,1420,1423,1425,1427],{"class":57,"line":58},[55,1409,62],{"class":61},[55,1411,66],{"class":65},[55,1413,1414],{"class":61},"> sync ",[55,1416,72],{"class":65},[55,1418,1419],{"class":61}," Collections.",[55,1421,1422],{"class":87},"synchronizedSet",[55,1424,91],{"class":61},[55,1426,933],{"class":65},[55,1428,1429],{"class":61}," HashSet\u003C>());\n",[55,1431,1432,1435,1438],{"class":57,"line":81},[55,1433,1434],{"class":65},"synchronized",[55,1436,1437],{"class":61}," (sync) {                       ",[55,1439,1440],{"class":101},"\u002F\u002F iteration STILL needs the lock\n",[55,1442,1443,1446,1449,1452,1455,1458],{"class":57,"line":105},[55,1444,1445],{"class":65},"    for",[55,1447,1448],{"class":61}," (String x ",[55,1450,1451],{"class":65},":",[55,1453,1454],{"class":61}," sync) { ",[55,1456,1457],{"class":101},"\u002F* ... *\u002F",[55,1459,1272],{"class":61},[55,1461,1462],{"class":57,"line":121},[55,1463,443],{"class":61},[55,1465,1466],{"class":57,"line":268},[55,1467,249],{"emptyLinePlaceholder":248},[55,1469,1470,1472,1474,1477,1479,1482,1485,1488],{"class":57,"line":412},[55,1471,62],{"class":61},[55,1473,66],{"class":65},[55,1475,1476],{"class":61},"> concurrent ",[55,1478,72],{"class":65},[55,1480,1481],{"class":61}," ConcurrentHashMap.",[55,1483,1484],{"class":87},"newKeySet",[55,1486,1487],{"class":61},"();   ",[55,1489,1490],{"class":101},"\u002F\u002F preferred default\n",[55,1492,1493,1495,1498,1501,1503,1505,1508],{"class":57,"line":440},[55,1494,62],{"class":61},[55,1496,1497],{"class":65},"Listener",[55,1499,1500],{"class":61},"> listeners ",[55,1502,72],{"class":65},[55,1504,75],{"class":65},[55,1506,1507],{"class":61}," CopyOnWriteArraySet\u003C>();    ",[55,1509,1510],{"class":101},"\u002F\u002F read-mostly, tiny\n",[15,1512,1513,1514,1516],{},"The key insight: a globally locked wrapper serializes every access, while\n",[19,1515,1391],{}," scales across threads. Pick the wrapper only for quick,\nlow-contention cases.",[10,1518,1520],{"id":1519},"set-algebra-union-intersection-difference","Set algebra — union, intersection, difference",[15,1522,1523,1524,1526,1527,1541,1542,1545],{},"The three classic set operations map directly onto bulk ",[19,1525,25],{}," methods: ",[28,1528,1529,1532,1533,1536,1537,1540],{},[19,1530,1531],{},"addAll","\nis union, ",[19,1534,1535],{},"retainAll"," is intersection, ",[19,1538,1539],{},"removeAll"," is difference",". The one discipline to\nremember is to ",[28,1543,1544],{},"copy first",", so you don't silently mutate the caller's set.",[46,1547,1549],{"className":48,"code":1548,"language":50,"meta":51,"style":51},"Set\u003CInteger> a = new HashSet\u003C>(List.of(1, 2, 3));\nSet\u003CInteger> b = new HashSet\u003C>(List.of(3, 4, 5));\n\nSet\u003CInteger> union = new HashSet\u003C>(a); union.addAll(b);          \u002F\u002F {1,2,3,4,5}\nSet\u003CInteger> inter = new HashSet\u003C>(a); inter.retainAll(b);       \u002F\u002F {3}\nSet\u003CInteger> diff  = new HashSet\u003C>(a); diff.removeAll(b);        \u002F\u002F {1,2}\n",[19,1550,1551,1586,1619,1623,1647,1671],{"__ignoreMap":51},[55,1552,1553,1555,1557,1560,1562,1564,1567,1569,1571,1574,1576,1579,1581,1584],{"class":57,"line":58},[55,1554,62],{"class":61},[55,1556,635],{"class":65},[55,1558,1559],{"class":61},"> a ",[55,1561,72],{"class":65},[55,1563,75],{"class":65},[55,1565,1566],{"class":61}," HashSet\u003C>(List.",[55,1568,648],{"class":87},[55,1570,91],{"class":61},[55,1572,1573],{"class":430},"1",[55,1575,161],{"class":61},[55,1577,1578],{"class":430},"2",[55,1580,161],{"class":61},[55,1582,1583],{"class":430},"3",[55,1585,671],{"class":61},[55,1587,1588,1590,1592,1595,1597,1599,1601,1603,1605,1607,1609,1612,1614,1617],{"class":57,"line":81},[55,1589,62],{"class":61},[55,1591,635],{"class":65},[55,1593,1594],{"class":61},"> b ",[55,1596,72],{"class":65},[55,1598,75],{"class":65},[55,1600,1566],{"class":61},[55,1602,648],{"class":87},[55,1604,91],{"class":61},[55,1606,1583],{"class":430},[55,1608,161],{"class":61},[55,1610,1611],{"class":430},"4",[55,1613,161],{"class":61},[55,1615,1616],{"class":430},"5",[55,1618,671],{"class":61},[55,1620,1621],{"class":57,"line":105},[55,1622,249],{"emptyLinePlaceholder":248},[55,1624,1625,1627,1629,1632,1634,1636,1639,1641,1644],{"class":57,"line":121},[55,1626,62],{"class":61},[55,1628,635],{"class":65},[55,1630,1631],{"class":61},"> union ",[55,1633,72],{"class":65},[55,1635,75],{"class":65},[55,1637,1638],{"class":61}," HashSet\u003C>(a); union.",[55,1640,1531],{"class":87},[55,1642,1643],{"class":61},"(b);          ",[55,1645,1646],{"class":101},"\u002F\u002F {1,2,3,4,5}\n",[55,1648,1649,1651,1653,1656,1658,1660,1663,1665,1668],{"class":57,"line":268},[55,1650,62],{"class":61},[55,1652,635],{"class":65},[55,1654,1655],{"class":61},"> inter ",[55,1657,72],{"class":65},[55,1659,75],{"class":65},[55,1661,1662],{"class":61}," HashSet\u003C>(a); inter.",[55,1664,1535],{"class":87},[55,1666,1667],{"class":61},"(b);       ",[55,1669,1670],{"class":101},"\u002F\u002F {3}\n",[55,1672,1673,1675,1677,1680,1682,1684,1687,1689,1692],{"class":57,"line":412},[55,1674,62],{"class":61},[55,1676,635],{"class":65},[55,1678,1679],{"class":61},"> diff  ",[55,1681,72],{"class":65},[55,1683,75],{"class":65},[55,1685,1686],{"class":61}," HashSet\u003C>(a); diff.",[55,1688,1539],{"class":87},[55,1690,1691],{"class":61},"(b);        ",[55,1693,1694],{"class":101},"\u002F\u002F {1,2}\n",[15,1696,1697,1698,1701,1702,1705],{},"For intersection, iterate the ",[28,1699,1700],{},"smaller"," set against the larger — the cost is roughly\nO(min(a, b)) O(1) lookups. A ",[41,1703,1704],{},"symmetric"," difference (in either but not both) is just union\nminus intersection.",[10,1707,1709],{"id":1708},"the-mutable-element-pitfall","The mutable-element pitfall",[15,1711,17,1712,1714,1715,450,1717,1720,1721,453,1723,1725,1726,1728,1729,195],{},[19,1713,160],{}," places each element in the bucket chosen by its ",[19,1716,170],{},[28,1718,1719],{},"at insertion\ntime",". If you later mutate a field that ",[19,1722,465],{},[19,1724,300],{}," depend on, the element's hash\nchanges but it stays in its old bucket — and the set can no longer find it. The same logic\nbreaks ",[19,1727,180],{}," if you mutate a field used by ",[19,1730,307],{},[46,1732,1734],{"className":48,"code":1733,"language":50,"meta":51,"style":51},"Set\u003CPoint> set = new HashSet\u003C>();\nPoint p = new Point(1, 1);\nset.add(p);\np.x = 99;                  \u002F\u002F mutated a field used by hashCode\n\nset.contains(p);           \u002F\u002F false — looks in the new bucket, finds nothing\n\u002F\u002F p is now a \"ghost\": present but unreachable; even remove() may miss it\n",[19,1735,1736,1751,1772,1782,1798,1802,1814],{"__ignoreMap":51},[55,1737,1738,1740,1742,1745,1747,1749],{"class":57,"line":58},[55,1739,62],{"class":61},[55,1741,912],{"class":65},[55,1743,1744],{"class":61},"> set ",[55,1746,72],{"class":65},[55,1748,75],{"class":65},[55,1750,78],{"class":61},[55,1752,1753,1756,1758,1760,1762,1764,1766,1768,1770],{"class":57,"line":81},[55,1754,1755],{"class":61},"Point p ",[55,1757,72],{"class":65},[55,1759,75],{"class":65},[55,1761,896],{"class":87},[55,1763,91],{"class":61},[55,1765,1573],{"class":430},[55,1767,161],{"class":61},[55,1769,1573],{"class":430},[55,1771,548],{"class":61},[55,1773,1774,1777,1779],{"class":57,"line":105},[55,1775,1776],{"class":61},"set.",[55,1778,88],{"class":87},[55,1780,1781],{"class":61},"(p);\n",[55,1783,1784,1787,1789,1792,1795],{"class":57,"line":121},[55,1785,1786],{"class":61},"p.x ",[55,1788,72],{"class":65},[55,1790,1791],{"class":430}," 99",[55,1793,1794],{"class":61},";                  ",[55,1796,1797],{"class":101},"\u002F\u002F mutated a field used by hashCode\n",[55,1799,1800],{"class":57,"line":268},[55,1801,249],{"emptyLinePlaceholder":248},[55,1803,1804,1806,1808,1811],{"class":57,"line":412},[55,1805,1776],{"class":61},[55,1807,38],{"class":87},[55,1809,1810],{"class":61},"(p);           ",[55,1812,1813],{"class":101},"\u002F\u002F false — looks in the new bucket, finds nothing\n",[55,1815,1816],{"class":57,"line":440},[55,1817,1818],{"class":101},"\u002F\u002F p is now a \"ghost\": present but unreachable; even remove() may miss it\n",[15,1820,1821,1822,1825,1826,1828],{},"This is why set elements (and map keys) should be ",[28,1823,1824],{},"immutable",", or at least never mutated\non their equality fields while stored. It's exactly why ",[19,1827,66],{}," and the wrapper types —\nimmutable by design — make flawless set elements.",[10,1830,1832],{"id":1831},"recap","Recap",[15,1834,17,1835,1837,1838,1841,1842,1845,1846,1852,1853,1857,1858,1860,1861,1863,1864,1866,1867,1869,1870,1875,1876,1878,1879,1883,1884,1888,1889,803,1891,453,1893,1895,1896,1898],{},[19,1836,21],{}," is defined by one rule, ",[28,1839,1840],{},"no duplicates",", and the whole topic hinges on ",[41,1843,1844],{},"how","\nduplication is judged: ",[28,1847,1848,453,1850],{},[19,1849,300],{},[19,1851,465],{}," for the hash-based sets, ",[28,1854,1855],{},[19,1856,307],{}," for\nthe tree. ",[19,1859,160],{}," (HashMap-backed, O(1), unordered) is your default; ",[19,1862,164],{},"\nadds ",[28,1865,492],{}," for the price of a few links; ",[19,1868,180],{}," (red-black tree, O(log n))\ngives ",[28,1871,1872,1873],{},"sorted order plus ",[19,1874,771],{}," navigation, but is null-hostile and demands a\n",[19,1877,190],{}," for custom types. Reach for ",[28,1880,1881],{},[19,1882,1206],{}," with enum keys and\n",[28,1885,1886],{},[19,1887,1391],{}," under concurrency. Compose sets with ",[19,1890,1531],{},[19,1892,1535],{},[19,1894,1539],{},", and keep elements ",[28,1897,1824],{}," so they never go missing.",[1900,1901,1902],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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}",{"title":51,"searchDepth":81,"depth":81,"links":1904},[1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916],{"id":12,"depth":81,"text":13},{"id":145,"depth":81,"text":146},{"id":317,"depth":81,"text":318},{"id":471,"depth":81,"text":472},{"id":591,"depth":81,"text":592},{"id":810,"depth":81,"text":811},{"id":1013,"depth":81,"text":1014},{"id":1195,"depth":81,"text":1196},{"id":1372,"depth":81,"text":1373},{"id":1519,"depth":81,"text":1520},{"id":1708,"depth":81,"text":1709},{"id":1831,"depth":81,"text":1832},"A deep guide to Java's Set implementations — HashSet, LinkedHashSet and TreeSet — covering dedup via equals\u002FhashCode and compareTo, ordering, NavigableSet, EnumSet, concurrent sets and set algebra.","medium","md","Java",{},"\u002Fblog\u002Fjava-set-hashset-treeset-linkedhashset","\u002Fjava\u002Fcollections\u002Fset-implementations",{"title":5,"description":1917},"blog\u002Fjava-set-hashset-treeset-linkedhashset","Set Implementations","Collections","collections","2026-06-20","eokN6ukheRZPNszfdmGga9OUz_EJxKc0ZaSLUhBeEko",1782244090186]