[{"data":1,"prerenderedAt":142},["ShallowReactive",2],{"qa-\u002Fjava\u002Fcollections\u002Fset-implementations":3},{"page":4,"siblings":122,"blog":139},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":113,"related":114,"seo":115,"seoDescription":116,"stem":117,"subtopic":6,"topic":118,"topicSlug":119,"updated":120,"__hash__":121},"qa\u002Fjava\u002Fcollections\u002Fset-implementations.md","Set Implementations",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,3,"\u002Fjava\u002Fcollections\u002Fset-implementations",[23,28,32,36,40,44,48,52,56,61,65,69,73,77,81,85,89,93,97,101,105,109],{"id":24,"difficulty":25,"q":26,"a":27},"set-contract","easy","What is the Set interface and what is its core contract?","A `Set` is a `Collection` that holds **no duplicate elements** — adding an\nelement already present is a no-op and `add` returns `false`. There's no index\nand (for the general contract) no positional access; you check membership with\n`contains`, which is the operation sets are built to make fast.\n\n```java\nSet\u003CString> tags = new HashSet\u003C>();\ntags.add(\"java\");   \u002F\u002F true  — added\ntags.add(\"java\");   \u002F\u002F false — duplicate, ignored\ntags.size();        \u002F\u002F 1\ntags.contains(\"java\"); \u002F\u002F true\n```\n\n\"Duplicate\" is defined by **`equals`** (and `hashCode` for hash-based sets, or\n`compareTo`\u002F`Comparator` for sorted sets) — not by reference identity. That\ndefinition of equality is the heart of every Set question.\n",{"id":29,"difficulty":14,"q":30,"a":31},"how-dedup-works","How does a Set decide that two elements are duplicates?","It depends on the implementation, and this is the single most important Set\ndetail in interviews:\n\n| Set | Duplicate test |\n| --- | -------------- |\n| `HashSet`, `LinkedHashSet` | `hashCode()` to find the bucket, then `equals()` within it |\n| `TreeSet` | `compareTo()` (or the supplied `Comparator`) — **`equals` is ignored** |\n\n```java\n\u002F\u002F Two elements are \"equal\" in a TreeSet when compare returns 0:\nSet\u003CString> ci = new TreeSet\u003C>(String.CASE_INSENSITIVE_ORDER);\nci.add(\"Java\");\nci.add(\"JAVA\");   \u002F\u002F compare == 0 -> treated as duplicate, not added\nci.size();        \u002F\u002F 1\n```\n\nThe trap: a `TreeSet` uses comparison, **not `equals`**, so an element can be\ndropped as a \"duplicate\" even though `equals` says it's different (and vice\nversa) — keep your `compareTo` *consistent with equals* to avoid surprises.\n",{"id":33,"difficulty":14,"q":34,"a":35},"hashset-internals","How does HashSet work internally?","A `HashSet` is just a thin wrapper around a **`HashMap`** — each element is\nstored as a **key**, and all keys map to the same dummy `PRESENT` value object.\nSo everything you know about `HashMap` (buckets, load factor, treeified bins)\napplies directly.\n\n```java\n\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 if key was new\n}\n```\n\nThat's why `HashSet` gives **average O(1)** `add`\u002F`contains`\u002F`remove`, has **no\nordering**, and depends entirely on good `hashCode`\u002F`equals`. If hashes collide\nbadly, performance degrades toward O(n) (or O(log n) once a bucket treeifies).\n",{"id":37,"difficulty":14,"q":38,"a":39},"linkedhashset","How is LinkedHashSet different from HashSet?","`LinkedHashSet` extends `HashSet` but is backed by a **`LinkedHashMap`**, so it\nkeeps a doubly-linked list threading through the entries. The effect:\niteration follows **insertion order** while keeping `HashSet`'s O(1) operations.\n\n```java\nSet\u003CString> hs = new HashSet\u003C>();\nhs.add(\"c\"); hs.add(\"a\"); hs.add(\"b\");\n\u002F\u002F iteration order: unspecified (e.g. a, b, c)\n\nSet\u003CString> lhs = new LinkedHashSet\u003C>();\nlhs.add(\"c\"); lhs.add(\"a\"); lhs.add(\"b\");\n\u002F\u002F iteration order: c, a, b  — exactly as inserted\n```\n\nCost is a slightly larger memory footprint (the extra prev\u002Fnext links). Reach\nfor it whenever you need **dedup but predictable, reproducible ordering** —\ne.g. removing duplicates from a list without scrambling it.\n",{"id":41,"difficulty":14,"q":42,"a":43},"treeset-internals","How does TreeSet work and what ordering does it give?","A `TreeSet` is backed by a **`TreeMap`**, a **red-black tree** (self-balancing\nbinary search tree). Elements are kept in **sorted order** — natural ordering\nvia `Comparable`, or a `Comparator` you pass to the constructor — and core\noperations are **O(log n)** rather than O(1).\n\n```java\nSet\u003CInteger> nums = new TreeSet\u003C>();\nnums.add(5); nums.add(1); nums.add(3);\nSystem.out.println(nums);  \u002F\u002F [1, 3, 5] — always sorted\n\n\u002F\u002F custom order:\nSet\u003CString> byLen = new TreeSet\u003C>(Comparator.comparingInt(String::length));\n```\n\nBecause it's a tree, you get **range and neighbour queries** for free\n(`first`, `last`, `floor`, `ceiling`, `headSet`…). The trade-off vs `HashSet`:\nslower point lookups but ordered iteration and rich navigation.\n",{"id":45,"difficulty":14,"q":46,"a":47},"treeset-requires-comparable","What does TreeSet require of its elements?","Elements must be **mutually comparable** — either they implement `Comparable`\n(natural ordering) or you supply a `Comparator`. Without one of those, the very\nfirst `add` of a non-comparable type throws `ClassCastException` at runtime.\n\n```java\nclass Point { int x, y; }              \u002F\u002F no Comparable, no Comparator\nSet\u003CPoint> s = new TreeSet\u003C>();\ns.add(new Point());                    \u002F\u002F ClassCastException at runtime\n\n\u002F\u002F fix: give it an ordering\nSet\u003CPoint> ok = new TreeSet\u003C>(Comparator.comparingInt(p -> p.x));\nok.add(new Point());                   \u002F\u002F fine\n```\n\nNote the failure is **not** at construction time — an empty `TreeSet` is happy;\nit only blows up when it needs to compare an element it can't order.\n",{"id":49,"difficulty":14,"q":50,"a":51},"comparison-table","Compare HashSet, LinkedHashSet and TreeSet.","The classic side-by-side every interviewer expects:\n\n| Feature | `HashSet` | `LinkedHashSet` | `TreeSet` |\n| ------- | --------- | --------------- | --------- |\n| Backed by | `HashMap` | `LinkedHashMap` | `TreeMap` (red-black tree) |\n| Ordering | none | insertion order | sorted (natural\u002Fcomparator) |\n| `add`\u002F`contains`\u002F`remove` | O(1) avg | O(1) avg | O(log n) |\n| `null` allowed | one `null` | one `null` | **no** (NPE with natural order) |\n| Needs `equals`\u002F`hashCode` | yes | yes | uses `compareTo`\u002F`Comparator` |\n| Extra navigation | no | no | yes (`first`\u002F`floor`\u002F`subSet`…) |\n\n```java\n\u002F\u002F pick by need:\nnew HashSet\u003C>();        \u002F\u002F fastest, order doesn't matter\nnew LinkedHashSet\u003C>();  \u002F\u002F dedup, keep insertion order\nnew TreeSet\u003C>();        \u002F\u002F need sorted iteration or range queries\n```\n\nNone of the three is thread-safe. **Default to `HashSet`**; upgrade only when\nyou specifically need order or sorting.\n",{"id":53,"difficulty":14,"q":54,"a":55},"sortedset-vs-navigableset","What is the difference between SortedSet and NavigableSet?","`SortedSet` is the older interface guaranteeing sorted iteration and offering\n`first()`, `last()`, `headSet`, `tailSet`, `subSet`, and `comparator()`.\n`NavigableSet` (Java 6+) **extends** it with neighbour lookups and\nreverse-order views. `TreeSet` implements both.\n\n```java\nNavigableSet\u003CInteger> s = new TreeSet\u003C>(List.of(10, 20, 30, 40));\ns.first();           \u002F\u002F 10        (SortedSet)\ns.last();            \u002F\u002F 40\ns.floor(25);         \u002F\u002F 20  \u003C= 25 (NavigableSet)\ns.ceiling(25);       \u002F\u002F 30  >= 25\ns.descendingSet();   \u002F\u002F [40, 30, 20, 10]\ns.pollFirst();       \u002F\u002F 10, and removes it\n```\n\nIn practice you usually just declare the variable as `NavigableSet` (or\n`TreeSet`) to get the full method set — `SortedSet` alone lacks the handy\n`floor`\u002F`ceiling`\u002F`higher`\u002F`lower` queries.\n",{"id":57,"difficulty":58,"q":59,"a":60},"navigation-methods","hard","What do floor, ceiling, higher and lower do on a NavigableSet?","They find the closest element to a target. The distinction is **inclusive vs\nexclusive** of the target itself:\n\n| Method | Returns |\n| ------ | ------- |\n| `floor(e)` | greatest element **≤ e** |\n| `ceiling(e)` | smallest element **≥ e** |\n| `lower(e)` | greatest element **strictly \u003C e** |\n| `higher(e)` | smallest element **strictly > e** |\n\n```java\nNavigableSet\u003CInteger> s = new TreeSet\u003C>(List.of(10, 20, 30));\ns.floor(20);   \u002F\u002F 20  (\u003C=)\ns.lower(20);   \u002F\u002F 10  (strictly \u003C)\ns.ceiling(20); \u002F\u002F 20  (>=)\ns.higher(20);  \u002F\u002F 30  (strictly >)\ns.floor(5);    \u002F\u002F null — nothing \u003C= 5\n```\n\nAll return `null` when no such element exists, so callers must null-check.\nThese run in **O(log n)** and are exactly why you reach for a `TreeSet` over a\n`HashSet` for \"nearest neighbour\" or range problems.\n",{"id":62,"difficulty":58,"q":63,"a":64},"subset-views","What do headSet, tailSet and subSet return, and are they live views?","They return **range views** of a sorted set — not copies. `headSet(to)` is\neverything below `to`, `tailSet(from)` is everything from `from` up, and\n`subSet(from, to)` is the half-open range `[from, to)`. The `NavigableSet`\noverloads let you toggle the inclusive\u002Fexclusive bounds.\n\n```java\nNavigableSet\u003CInteger> s = new TreeSet\u003C>(List.of(10, 20, 30, 40));\ns.headSet(30);            \u002F\u002F [10, 20]      (exclusive end)\ns.tailSet(20);            \u002F\u002F [20, 30, 40]  (inclusive start)\ns.subSet(20, 40);         \u002F\u002F [20, 30]      (half-open)\ns.subSet(20, true, 40, true); \u002F\u002F [20, 30, 40]\n```\n\nThey are **backed by the original set**: changes flow both ways, and adding an\nelement outside the view's bounds throws `IllegalArgumentException`. Wrap in a\n`new TreeSet\u003C>(view)` if you need an independent snapshot.\n",{"id":66,"difficulty":14,"q":67,"a":68},"descendingset","How do you iterate a TreeSet in reverse order?","Use `descendingSet()` (a reverse-ordered **view**) or `descendingIterator()`.\nBoth walk the tree from largest to smallest without copying or re-sorting.\n\n```java\nNavigableSet\u003CInteger> s = new TreeSet\u003C>(List.of(1, 2, 3));\nfor (int n : s.descendingSet()) {\n    System.out.print(n + \" \");   \u002F\u002F 3 2 1\n}\n\nIterator\u003CInteger> it = s.descendingIterator();\nwhile (it.hasNext()) { \u002F* 3, then 2, then 1 *\u002F }\n```\n\nBecause `descendingSet()` is a live view, mutations on it affect the original\nset and vice versa. It's the idiomatic alternative to constructing a\n`TreeSet` with `Comparator.reverseOrder()` when you only need reverse\niteration occasionally.\n",{"id":70,"difficulty":14,"q":71,"a":72},"treeset-null","Can a TreeSet contain null?","No — with natural ordering, adding `null` throws **`NullPointerException`**,\nbecause the set must call `compareTo` on the element and `null.compareTo(...)`\ncan't happen. (A `HashSet`\u002F`LinkedHashSet`, by contrast, allows a single\n`null`.)\n\n```java\nSet\u003CString> tree = new TreeSet\u003C>();\ntree.add(null);   \u002F\u002F NullPointerException\n\nSet\u003CString> hash = new HashSet\u003C>();\nhash.add(null);   \u002F\u002F fine — exactly one null allowed\n```\n\nEven a null-tolerant `Comparator` (e.g. `Comparator.nullsFirst(...)`) only\nhelps for *non-first* elements; the first `add(null)` on an empty natural-order\n`TreeSet` still throws. Treat `TreeSet` as **null-hostile**.\n",{"id":74,"difficulty":14,"q":75,"a":76},"enumset","What is EnumSet and why is it so fast?","`EnumSet` is a specialized `Set` for **enum** types only. Internally it's a\n**bit vector** — a single `long` (or array of `long`s for big enums) where each\nbit represents one constant. That makes operations near-instant and the memory\nfootprint tiny.\n\n```java\nenum Day { MON, TUE, WED, THU, FRI, SAT, SUN }\n\nEnumSet\u003CDay> work = EnumSet.range(Day.MON, Day.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```\n\nIteration follows the enum's **declaration order**. It's created via factory\nmethods (`of`, `range`, `allOf`, `noneOf`, `complementOf`) rather than `new`.\nWhenever your set elements are enum constants, `EnumSet` beats `HashSet` on\nevery axis — speed, memory, and clarity.\n",{"id":78,"difficulty":58,"q":79,"a":80},"set-thread-safety","How do you make a Set thread-safe?","The standard `HashSet`\u002F`TreeSet`\u002F`LinkedHashSet` are **not synchronized**.\nOptions, from cheap to concurrent:\n\n- **`Collections.synchronizedSet(set)`** — wraps every method in a lock; you\n  must still **manually synchronize** when *iterating*.\n- **`ConcurrentHashMap.newKeySet()`** — a concurrent hash set backed by\n  `ConcurrentHashMap`; high-throughput, no external locking, weakly consistent\n  iteration.\n- **`CopyOnWriteArraySet`** — copies the backing array on every write; great\n  for tiny, read-heavy sets, terrible for write-heavy ones.\n\n```java\nSet\u003CString> sync = Collections.synchronizedSet(new HashSet\u003C>());\nsynchronized (sync) {                \u002F\u002F iteration needs the lock\n    for (String s : sync) { \u002F* ... *\u002F }\n}\n\nSet\u003CString> concurrent = ConcurrentHashMap.newKeySet(); \u002F\u002F preferred default\n```\n\nFor most concurrent code, **`ConcurrentHashMap.newKeySet()`** is the right\nchoice — it scales far better than a globally locked wrapper.\n",{"id":82,"difficulty":58,"q":83,"a":84},"copyonwritearrayset","When would you use CopyOnWriteArraySet?","`CopyOnWriteArraySet` (backed by a `CopyOnWriteArrayList`) makes a **fresh copy\nof the whole array on every mutation**. Reads and iteration are lock-free and\nsee a stable snapshot, but each `add`\u002F`remove` is **O(n)**.\n\n```java\nSet\u003CListener> listeners = new CopyOnWriteArraySet\u003C>();\nlisteners.add(a);                 \u002F\u002F copies the backing array\nfor (Listener l : listeners) {    \u002F\u002F iterates a snapshot — no CME, ever\n    l.onEvent();                  \u002F\u002F safe even if another thread mutates\n}\n```\n\nIt shines for **small, read-mostly, rarely-mutated** sets — the textbook case\nbeing event-listener registries. Because it scans the array, `contains` is\nO(n), so it's a poor fit for large or write-heavy sets, where\n`ConcurrentHashMap.newKeySet()` wins.\n",{"id":86,"difficulty":25,"q":87,"a":88},"set-union","How do you compute the union of two sets?","Copy one set and `addAll` the other — duplicates are dropped automatically by\nthe Set contract.\n\n```java\nSet\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);\nunion.addAll(b);          \u002F\u002F {1, 2, 3, 4, 5}\n```\n\n`addAll` is the **union** operator. Always copy first (`new HashSet\u003C>(a)`) so\nyou don't mutate the original `a` — a common bug is calling `a.addAll(b)` and\nsilently changing the caller's set.\n",{"id":90,"difficulty":25,"q":91,"a":92},"set-intersection","How do you compute the intersection of two sets?","`retainAll` keeps only the elements present in **both** sets.\n\n```java\nSet\u003CInteger> a = new HashSet\u003C>(List.of(1, 2, 3));\nSet\u003CInteger> b = new HashSet\u003C>(List.of(3, 4, 5));\n\nSet\u003CInteger> intersection = new HashSet\u003C>(a);\nintersection.retainAll(b);   \u002F\u002F {3}\n```\n\n`retainAll` is the **intersection** operator. For performance, copy and iterate\nthe **smaller** set against the larger one — the cost is roughly\nO(min(a, b)) lookups, and lookups are O(1) on a `HashSet`.\n",{"id":94,"difficulty":25,"q":95,"a":96},"set-difference","How do you compute the difference between two sets?","`removeAll` strips out every element that also appears in the other set,\nleaving the **difference** (elements in `a` but not `b`).\n\n```java\nSet\u003CInteger> a = new HashSet\u003C>(List.of(1, 2, 3));\nSet\u003CInteger> b = new HashSet\u003C>(List.of(3, 4, 5));\n\nSet\u003CInteger> diff = new HashSet\u003C>(a);\ndiff.removeAll(b);    \u002F\u002F {1, 2}\n```\n\nSo the trio is: **`addAll` = union, `retainAll` = intersection,\n`removeAll` = difference**. A symmetric difference (in either but not both) is\njust union minus intersection, or two `removeAll`s combined.\n",{"id":98,"difficulty":58,"q":99,"a":100},"mutable-element-breaks-hashset","Why can mutating an element break a HashSet?","A `HashSet` places each element in a **bucket chosen by its `hashCode()` at\ninsertion time**. If you then mutate a field that `hashCode`\u002F`equals` depend\non, the element's hash changes but it stays in the *old* bucket — so the set\ncan no longer find it.\n\n```java\nSet\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```\n\nThat's why Set (and Map-key) elements should be **immutable**, or at least\nnever mutated on their `equals`\u002F`hashCode` fields while stored. It's the same\nreason `String` and the wrapper types — immutable — make perfect set elements.\n",{"id":102,"difficulty":14,"q":103,"a":104},"immutable-set-of","What does Set.of return and what happens with duplicates?","`Set.of(...)` (Java 9+) returns a small, **immutable** set. Any mutating call\n(`add`, `remove`, `clear`) throws `UnsupportedOperationException`, and — unlike\na normal `add` that silently ignores dups — passing a **duplicate to the\nfactory throws `IllegalArgumentException`**.\n\n```java\nSet\u003CString> s = Set.of(\"a\", \"b\", \"c\");\ns.add(\"d\");                    \u002F\u002F UnsupportedOperationException\n\nSet\u003CString> dup = Set.of(\"a\", \"a\");  \u002F\u002F IllegalArgumentException at creation!\n```\n\nIt also **rejects `null`** (NPE) and has an **unspecified iteration order**\nthat can vary between runs. Use it for compact, never-changing constant sets;\nuse `LinkedHashSet`\u002F`TreeSet` when you need order or mutability.\n",{"id":106,"difficulty":14,"q":107,"a":108},"set-iteration-order","What ordering guarantees do the different Sets give during iteration?","Each implementation makes a different promise — knowing them prevents a class\nof \"works on my machine\" bugs:\n\n| Set | Iteration order |\n| --- | --------------- |\n| `HashSet` | **no guarantee** (can even change between JVM runs) |\n| `LinkedHashSet` | insertion order |\n| `TreeSet` | sorted (natural or comparator) |\n| `EnumSet` | enum declaration order |\n| `Set.of(...)` | unspecified, may vary per run |\n\n```java\n\u002F\u002F never rely on HashSet order:\nfor (String s : new HashSet\u003C>(List.of(\"a\", \"b\", \"c\"))) { \u002F* any order *\u002F }\n```\n\nThe rule of thumb: if your output or tests depend on order, **don't use\n`HashSet`** — choose `LinkedHashSet` for insertion order or `TreeSet` for\nsorted order.\n",{"id":110,"difficulty":14,"q":111,"a":112},"contains-performance","Why is HashSet.contains so much faster than List.contains?","`HashSet.contains` hashes the element, jumps straight to one bucket, and checks\nonly the few items there — **average O(1)**. `List.contains` has no index into\nits data, so it scans from the front comparing each element — **O(n)**.\n\n```java\nList\u003CInteger> list = new ArrayList\u003C>(\u002F* 1_000_000 items *\u002F);\nSet\u003CInteger> set  = new HashSet\u003C>(list);\n\nlist.contains(999_999);   \u002F\u002F O(n) — walks ~a million elements\nset.contains(999_999);    \u002F\u002F O(1) — one bucket lookup\n```\n\nThis is why a frequent pattern is to **dump a list into a `HashSet` first** when\nyou'll do many membership checks. The rule of thumb: need fast \"is it in\nthere?\" — use a `Set`, not a `List`.\n",22,null,{"description":11},"Java Set interview questions — HashSet vs LinkedHashSet vs TreeSet, how they work internally, ordering and complexity, NavigableSet\u002FSortedSet, EnumSet and CopyOnWriteArraySet, and set operations.","java\u002Fcollections\u002Fset-implementations","Collections","collections","2026-06-20","CPWBeux7ocEGdCEYbf-oLAL2c2Pl2DvsbAXeVO6qsB0",[123,127,130,131,135],{"subtopic":124,"path":125,"order":126},"Lists, Maps & Sets","\u002Fjava\u002Fcollections\u002Flist-map-set",1,{"subtopic":128,"path":129,"order":12},"HashMap Internals","\u002Fjava\u002Fcollections\u002Fhashmap-internals",{"subtopic":6,"path":21,"order":20},{"subtopic":132,"path":133,"order":134},"Queue & Deque","\u002Fjava\u002Fcollections\u002Fqueue-deque",4,{"subtopic":136,"path":137,"order":138},"Comparable & Comparator","\u002Fjava\u002Fcollections\u002Fcomparable-comparator",5,{"path":140,"title":141},"\u002Fblog\u002Fjava-set-hashset-treeset-linkedhashset","Java Set Explained — HashSet vs LinkedHashSet vs TreeSet",1782244116215]