Why and how would you set the initial capacity of an ArrayList or HashMap when you know how many elements you will add?
answer
- Backing array is fixed length; resize = new array + copy all
- ArrayList: constructor arg = array length directly
- HashMap: resize when size > capacity * loadFactor (0.75)
- HashMap capacity = expectedSize / 0.75 + 1
- Java 19+: HashMap.newHashMap(n)
basics
~20 sThese collections grow by allocating a bigger backing array and copying everything over. If you know the size up front, pass it to the constructor (e.g. new ArrayList<>(1000)) so it allocates once instead of resizing many times.
solid answer
~40 sArray-backed collections store data in an internal array that has a fixed size. When it fills up, the collection allocates a larger array and copies all elements across, which costs time and produces garbage. If you know roughly how many elements you'll add, pass an initial capacity to the constructor so the array is big enough from the start and never (or rarely) resizes. For ArrayList, the constructor argument is the array length directly: new ArrayList<>(expectedSize). For HashMap it's subtler because of the load factor: the map resizes once size exceeds capacity * loadFactor (default 0.75), so to hold N entries without resizing you want capacity > N / 0.75 — practically new HashMap<>((int)(N / 0.75) + 1). This is a pure performance optimization; it never changes behaviour, only avoids repeated allocation and copying.
go deeper
Knows resizing means copying to a bigger array and that you can pass a size to the constructor to avoid it.
Distinguishes ArrayList (capacity = array length directly) from HashMap (capacity / load-factor), and applies the expectedSize / 0.75 idiom.
Explains the power-of-two rounding, the 1.5x vs 2x growth, and reaches for HashMap.newHashMap / Guava helpers; reasons about when it actually matters (hot paths, GC).
Frames it as an allocation/GC and latency concern at scale, weighs memory vs resize cost, and sets team conventions (always presize known-size maps) rather than micro-optimizing blindly.
## The problem: dynamic collections sit on fixed-size arrays A Java array (e.g. `Object[]`) has a **fixed length** chosen when it is created — you cannot grow it. But `ArrayList` and `HashMap` are *dynamic*: you can keep adding elements. They achieve this by holding an internal array (the **backing array**) and, when it gets full, **resizing**: allocating a new, larger array and copying every existing element into it. The old array becomes garbage. - **Capacity** = the length of the backing array = how many elements it can hold *before* it must resize. - **Size** = how many elements are actually stored right now. Resizing is the cost we want to avoid. Each resize is O(n) (copy all n elements) and allocates a whole new array. ## ArrayList `ArrayList` grows its backing array by roughly **1.5×** each time it fills (in modern JDKs: `newCap = oldCap + (oldCap >> 1)`). Starting from the default capacity of 10 and adding many elements, you trigger a sequence of resizes (10 → 15 → 22 → 33 → ...). Although the *total* copying work is still amortized O(n) overall, you pay multiple allocations and copies, and produce garbage that the GC must clean up. If you know you'll add ~1000 elements, do: ```java List<String> list = new ArrayList<>(1000); ``` Now the backing array is length 1000 immediately; the first 1000 `add` calls cause **zero** resizes. For `ArrayList`, the constructor argument is the array length *directly* — there's no load factor. ## HashMap — the load factor twist A `HashMap` stores entries in an array of **buckets**. The bucket for a key is chosen from its hash code. The **load factor** (default `0.75`) is the fraction of buckets that may be occupied before the map grows. The map resizes (doubling the bucket array) when: ``` size > capacity * loadFactor ``` So a map with default capacity 16 and load factor 0.75 resizes once `size > 12`. Capacity is always a **power of two** (16, 32, 64, ...); even if you ask for capacity 1000, HashMap rounds up to the next power of two (1024). The consequence: to store N entries without any resize, you must give a capacity such that `N <= capacity * 0.75`, i.e. `capacity >= N / 0.75`. The common idiom: ```java Map<String,Integer> map = new HashMap<>((int)(expectedSize / 0.75f) + 1); ``` In Java 19+, there's a factory that does the maths for you: `HashMap.newHashMap(expectedSize)` sizes the map to hold `expectedSize` entries without resizing. ## Why bother - **Fewer allocations and copies** → less CPU, lower latency, fewer GC pauses, especially in hot loops or large maps. - **It never changes correctness** — only performance. A wrong guess just means you resize anyway (too low) or waste some memory (too high). ## When NOT to bother - Tiny or short-lived collections — the default is fine. - When you genuinely don't know the size — guessing wildly high wastes memory. ## Summary rule of thumb - `ArrayList(expectedSize)` — pass the count directly. - `HashMap`/`HashSet` — pass `expectedSize / 0.75 + 1` (or use `HashMap.newHashMap` / `Maps.newHashMapWithExpectedSize` from Guava).
- If you call new HashMap<>(1000), how many entries can it hold before it resizes?1000 is rounded up to the next power of two, 1024. It resizes when size exceeds 1024 * 0.75 = 768. So it holds 768 entries before the first resize, not 1000 — which is why the idiom uses capacity = expectedSize / 0.75 + 1.
- Does setting initial capacity ever change program behaviour or output?No. It only affects how many internal resizes/allocations happen. Iteration order, contents, and correctness are unchanged; it's a pure performance optimization.
saying these in an interview costs you the question
- Thinking new HashMap<>(1000) holds 1000 entries before resizing — it actually resizes at ~750 (1024 * 0.75)
- Assuming ArrayList's capacity argument is also divided by a load factor — it isn't, only HashMap has a load factor
- Claiming initial capacity changes the result/ordering — it's purely a performance hint
- Believing HashMap honours any capacity — it rounds up to the next power of two