C#
Dictionary
AddRange
data structures
programming

Why doesn't Dictionary have AddRange?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Many developers expect Dictionary<TKey, TValue> to have an AddRange method because List<T> has one. The absence is intentional, not an oversight. Bulk insertion for dictionaries is ambiguous because duplicate keys require policy decisions that cannot be universally correct.

Why AddRange Is Straightforward for Lists but Not Dictionaries

List<T>.AddRange simply appends values. Duplicates are allowed, order is preserved, and there is no conflict model.

Dictionaries are different because keys must be unique. During bulk insert, duplicate keys create unavoidable policy questions:

  • Throw an error.
  • Skip duplicate entries.
  • Overwrite existing values.
  • Merge values by domain-specific rule.

Each policy is valid in some scenarios and wrong in others. The base API therefore exposes lower-level primitives so callers can choose explicitly.

Existing Dictionary Primitives Already Cover Bulk Scenarios

You can implement any range policy today with Add, indexer assignment, and TryAdd.

csharp
1using System;
2using System.Collections.Generic;
3
4var target = new Dictionary<string, int>
5{
6    ["A"] = 1,
7    ["B"] = 2
8};
9
10var incoming = new Dictionary<string, int>
11{
12    ["B"] = 20,
13    ["C"] = 3
14};
15
16foreach (var kv in incoming)
17{
18    if (!target.TryAdd(kv.Key, kv.Value))
19    {
20        Console.WriteLine($"Duplicate key: {kv.Key}");
21    }
22}

That pattern makes conflict handling explicit and observable.

Create Explicit Extension Methods by Policy

If your codebase needs convenience, define policy-specific helpers with clear names.

csharp
1using System.Collections.Generic;
2
3public static class DictionaryExtensions
4{
5    public static void AddRangeThrow<TKey, TValue>(
6        this IDictionary<TKey, TValue> target,
7        IEnumerable<KeyValuePair<TKey, TValue>> source)
8    {
9        foreach (var kv in source)
10        {
11            target.Add(kv.Key, kv.Value);
12        }
13    }
14
15    public static void AddRangeOverwrite<TKey, TValue>(
16        this IDictionary<TKey, TValue> target,
17        IEnumerable<KeyValuePair<TKey, TValue>> source)
18    {
19        foreach (var kv in source)
20        {
21            target[kv.Key] = kv.Value;
22        }
23    }
24
25    public static int AddRangeSkip<TKey, TValue>(
26        this IDictionary<TKey, TValue> target,
27        IEnumerable<KeyValuePair<TKey, TValue>> source)
28    {
29        int skipped = 0;
30        foreach (var kv in source)
31        {
32            if (!target.TryAdd(kv.Key, kv.Value))
33            {
34                skipped++;
35            }
36        }
37        return skipped;
38    }
39}

The method name now documents conflict semantics at call sites.

Merge Policy Example for Aggregation Workloads

Some domains need value merging rather than skip or overwrite.

csharp
1using System;
2using System.Collections.Generic;
3
4var totals = new Dictionary<string, int>
5{
6    ["error"] = 5,
7    ["warn"] = 3
8};
9
10var batch = new Dictionary<string, int>
11{
12    ["error"] = 2,
13    ["info"] = 8
14};
15
16foreach (var kv in batch)
17{
18    totals[kv.Key] = totals.TryGetValue(kv.Key, out var current)
19        ? current + kv.Value
20        : kv.Value;
21}
22
23Console.WriteLine(string.Join(",", totals));

This is exactly why a single built-in AddRange would be misleading.

Performance Considerations During Bulk Insert

Large dictionary loads can trigger repeated internal resizing if capacity is too small. When approximate size is known, pre-size dictionary.

csharp
var map = new Dictionary<int, string>(capacity: 200_000);

Pre-sizing improves throughput and reduces allocation churn during large merges.

Also ensure custom key types have stable and efficient GetHashCode implementations. Poor hash quality can degrade expected performance significantly.

API Design Guidance for Teams

Use one clear bulk policy per call path:

  • Config ingest usually throws on duplicates.
  • Cache warm-up usually overwrites.
  • Telemetry aggregation usually merges.
  • Data reconciliation often skips and reports duplicates.

Make policy explicit in helper names and metrics so behavior is auditable.

Common Pitfalls

  • Implementing a generic AddRange helper without naming duplicate-key behavior.
  • Using exception-based duplicate handling in hot loops.
  • Overwriting existing values silently when requirements expected strict uniqueness.
  • Ignoring pre-sizing for very large bulk loads.
  • Forgetting that custom key equality and hash behavior controls duplicate detection.

Summary

  • Dictionary has no built-in AddRange because duplicate-key semantics are context dependent.
  • Existing primitives already support all bulk insertion policies.
  • Policy-specific extension methods are the clearest and safest approach.
  • Merge, skip, overwrite, and throw modes should be explicit by use case.
  • Pre-sizing and correct key hashing improve bulk-load performance and reliability.

Course illustration
Course illustration

All Rights Reserved.