🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
java.lang.IllegalArgumentException: duplicate element: red
at java.base/java.util.ImmutableCollections$SetN.<init>(ImmutableCollections.java:1164)
at java.base/java.util.Set.of(Set.java:505)⚡ Quick Fix Works 80% of the time
De-duplicate through a HashSet before freezing.
Set<String> colors = Set.copyOf(new LinkedHashSet<>(List.of("red", "red", "blue")));🧠 Why this Happens
Tap to expand the deep technical explanation
Factory routing depends on arity: one or two elements take the Set12 fast path, whose constructor compares the pair directly and throws on equals() (captured at ImmutableCollections.java:1017); three or more build a SetN that probes slots open-addressing style while filling its fixed table (captured at ImmutableCollections.java:1164). Either way a collision with an EQUAL element breaks the uniqueness precondition, so construction throws with the offending element in the message rather than silently dropping data you may have believed was distinct. Case differences matter: "Red" and "red" are NOT duplicates under equals(), which surprises teams expecting normalization.
The HITEC City Parking Spot Analogy:
Printing unique-guest name badges: the printer jams and shows the duplicated name rather than seating two guests in one chair.
🔁 How to Reproduce Confirm this is your error
Set<String> s = Set.of("red", "blue", "red"); // duplicate element: red
🛠️ Solutions (5 Ways to Fix)
Dedupe through a HashSet first, then freeze
👉 Use this if source data may contain repeats and first-seen order matters.
LinkedHashSet keeps encounter order while collapsing equals() duplicates; Set.copyOf then wraps it immutably without re-checking collisions.
Set<Color> palette = Set.copyOf(new LinkedHashSet<>(rawColors));Stream distinct().collect(toUnmodifiableSet())
👉 Use this if already inside a stream pipeline.
distinct() applies equals/hashCode filtering mid-pipeline so the collector receives unique elements only.
import static java.util.stream.Collectors.toUnmodifiableSet;
Set<String> tags = rows.stream()
.map(Row::tag)
.distinct()
.collect(toUnmodifiableSet());Normalize case/format before de-duplicating
👉 Use this if "duplicates" differ only by case or whitespace.
equals() is exact; map elements through trim/lowercase (or a custom canonical form) BEFORE the dedupe step so near-duplicates actually collapse.
Set<String> normalized = raw.stream()
.map(s -> s.strip().toLowerCase(Locale.ROOT))
.collect(Collectors.toUnmodifiableSet());If duplicates are legitimate, use List.of instead
👉 Use this if multiplicity carries meaning (e.g. votes, log lines).
Lists allow repeats; forcing them into a Set loses information and triggers this exception. Choose the collection that matches the domain.
List<String> votes = List.of("yes", "no", "yes"); // repeats allowedFix equals/hashCode on custom types
👉 Use this if the collision was unintentional and involves your own classes.
Badly implemented equals/hashCode can make distinct objects appear equal (or vice versa). Records generate both correctly; IDE generators cover regular classes.
record Point(int x, int y) {} // correct equals/hashCode for free📋 Version Notes
No Set.of; Collections.unmodifiableSet(new LinkedHashSet<>(...)) silently DROPS duplicates instead of throwing.
Factory introduced with fail-fast duplicate detection; Map.of behaves analogously for duplicate keys.
Set.copyOf shares the same rules.
🛡️ How to Prevent This Next Time
Treat Set.of arguments as pre-validated data. Where input provenance is unknown, dedupe-first is the default pattern.