Why do List.of / Set.of / Map.of have many fixed-arity overloads plus a varargs form, and how do you build a larger immutable Map?
answer
- 0–10 fixed-arity overloads + varargs for 11+
- Varargs always allocates an array
- Small overloads store in fields, no array
- Map.of takes k,v pairs up to 10 entries
- Map.entry + Map.ofEntries for larger/clearer maps
basics
~20 sThey provide overloads for 0 to 10 elements so small collections avoid creating an array, which saves memory and speeds things up. For bigger collections there is a varargs version, and for large maps you use Map.entry plus Map.ofEntries.
solid answer
~50 sEach factory ships explicit overloads for 0 through 10 elements, plus a final varargs overload for more. The reason is performance: a varargs call always allocates an array to hold the arguments, and for the tiny collections these methods are typically used for, that allocation plus the array-backed storage is wasteful. The fixed-arity overloads let the JVM store elements in dedicated fields with no array, which is both smaller in memory and faster to construct. Map.of is special: since each entry needs a key and a value, the overloads take pairs (k1, v1, k2, v2, ...) up to 10 entries. Beyond that, or when you want clearer code, you use Map.entry(k, v) to make immutable entries and pass them to Map.ofEntries(...), which is itself varargs over Map.Entry. All results keep the same immutability and null-hostility guarantees regardless of which overload you hit.
code
java · 12 lines// Up to 10 entries: direct overload
Map<String, Integer> small = Map.of("Ann", 30, "Bob", 25);
// More entries (or clearer grouping): entry + ofEntries
Map<String, Integer> big = Map.ofEntries(
Map.entry("Ann", 30),
Map.entry("Bob", 25),
Map.entry("Cy", 40)
// ... as many as you need
);
// List/Set: 0..10 use exact overloads; 11+ fall to varargs(E...)go deeper
Knows there are overloads and a varargs form, and that Map uses key/value pairs.
Can use Map.ofEntries for large maps and knows the 10-element cutoff.
Explains the performance rationale (avoiding varargs array allocation and array-backed storage for small sizes) and that guarantees stay uniform across overloads.
Recognizes the pattern as a general API-design technique for hot, small-N call sites and weighs it against API surface bloat and maintenance cost.
## Terms first - *Overload*: multiple methods with the same name but different parameter lists. The compiler picks one based on the arguments you pass. - *Arity*: the number of parameters a method takes. "Fixed-arity" means an exact count (e.g., exactly 3); "varargs" means variable count. - *Varargs* (`String... args`): a method form that accepts any number of arguments. Under the hood Java collects them into an *array*, so calling a varargs method allocates that array. - *Allocation*: reserving heap memory for a new object (here, the argument array). Allocations cost time and create garbage the collector must later clean up. ## Why not just one varargs method? You *could* define only `List.of(E... elements)`. But these factories are used overwhelmingly for very small collections (2, 3, 4 elements). Every such call would: 1. allocate an array to carry the varargs, and 2. produce a collection internally backed by an array. That is two allocations and array indirection for what could be a couple of fields. ## The chosen design: many small overloads + one varargs fallback The JDK declares: ```java static <E> List<E> of(); // 0 static <E> List<E> of(E e1); // 1 static <E> List<E> of(E e1, E e2); // 2 ... up to ... static <E> List<E> of(E e1, ..., E e10); // 10 static <E> List<E> of(E... elements); // 11+ ``` For 0–10 elements the compiler binds to the exact overload. Those implementations store elements in *individual fields* (or a tiny specialized class), avoiding the varargs array entirely — less memory, faster construction, no needless garbage. Only when you exceed 10 do you fall through to the varargs version, where an array is unavoidable anyway. The same pattern applies to `Set.of`. For `Map.of`, each entry is a key+value, so the overloads take pairs: ```java Map.of(); // empty Map.of(k1, v1); // 1 entry Map.of(k1, v1, k2, v2); // 2 entries ... up to 10 entries (20 args) ... ``` ## Building a larger or clearer immutable Map: Map.entry + Map.ofEntries Past 10 entries, the flat `k, v, k, v` list becomes unreadable and there is no 11-pair overload. The solution is two methods: ```java Map<String,Integer> ages = Map.ofEntries( Map.entry("Ann", 30), Map.entry("Bob", 25), Map.entry("Cy", 40) ); ``` - `Map.entry(k, v)` returns an *immutable* `Map.Entry` (also null-hostile). - `Map.ofEntries(Map.Entry...)` is a varargs method that assembles those entries into an immutable map. This form is also nicer for readability even under 10 entries, because each key/value pair is visually grouped. ## Guarantees are uniform No matter which overload you reach — fixed-arity, varargs, or `ofEntries` — the result is the same kind of truly immutable, null-rejecting collection with unspecified iteration order for sets/maps. The overload count is purely an internal optimization; it never changes the contract. ## Practical guidance - ≤10 elements/entries: use the direct `of(...)` overload. - >10 map entries, or when you want grouped readability: use `Map.entry` + `Map.ofEntries`. - Don't over-think it for correctness — the optimization is automatic; just know why the API looks the way it does.
- Why not implement List.of with only a single varargs method?Because varargs always allocates a backing array, and these factories are dominated by tiny collections; fixed-arity overloads store elements in fields, avoiding that allocation and being faster and smaller.
- How do you build an immutable Map with 15 entries?Use Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...), since Map.of only has overloads up to 10 entries.
saying these in an interview costs you the question
- Thinking the overloads change immutability or null rules
- Assuming there is an unlimited Map.of(k,v,...) for any size
- Believing varargs is free (it allocates an array)
- Trying Map.of with 11+ pairs instead of Map.ofEntries