C# dictionary

Création & initialisation

Basique

var d = new Dictionary<string, int>();
d["alice"] = 25;              // ajoute (ou met à jour)
d.Add("bob", 30);             // ajoute (exception si la clé existe)

Capacité initiale

var d = new Dictionary<string, int>(capacity: 1_000);

Comparateur (OrdinalIgnoreCase)

var d = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
d["Paris"] = 75;
Console.WriteLine(d.ContainsKey("paris"));   // True

Initialisation par collection

var d = new Dictionary<int, string>
{
    [1] = "un",
    [2] = "deux"
};

Depuis une séquence (LINQ ToDictionary)

var fruits = new[] { "pomme", "banane", "kiwi" };
var d = fruits.ToDictionary(
    keySelector: f => f,
    elementSelector: f => f.Length);

Ajouter / Mettre à jour

Ajout, TryAdd, indexeur

var d = new Dictionary<string, int>();

d.Add("alice", 25);

bool added = d.TryAdd("alice", 26);   // false si déjà présent

d["alice"] = 27;                      // upsert

Pattern Upsert (TryGetValue)

if (d.TryGetValue("bob", out int age))
    d["bob"] = age + 1;
else
    d["bob"] = 30;

Rechercher / Lire

TryGetValue & GetValueOrDefault

if (d.TryGetValue("alice", out int age))
{
    Console.WriteLine(age);
}

int valueOrDefault = d.GetValueOrDefault("charlie", -1);
// Attention: d["clé"] lève KeyNotFoundException si la clé est absente.

Itération

Parcourir KeyValuePair

foreach (var kv in d)
{
    Console.WriteLine($"{kv.Key} => {kv.Value}");
}

Déconstruction (C# 7+)

foreach (var (key, val) in d)
{
    Console.WriteLine($"{key} => {val}");
}

Itérer clés et valeurs séparément

foreach (var k in d.Keys) { /* ... */ }
foreach (var v in d.Values) { /* ... */ }

Suppression / Nettoyage

Remove, Remove(out), Clear

d.Remove("alice");
bool removed = d.Remove("bob", out int oldValue);

d.Clear();

Capacité

EnsureCapacity & TrimExcess

var d = new Dictionary<string, int>(capacity: 10_000);

d.EnsureCapacity(50_000);
d.TrimExcess();

Comparateurs de clé

Ignorer la casse (strings)

var d = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

Comparateur personnalisé (IEqualityComparer)

class Point2D
{
    public int X { get; init; }
    public int Y { get; init; }
}

class PointComparer : IEqualityComparer<Point2D>
{
    public bool Equals(Point2D? a, Point2D? b)
        => a is not null && b is not null && a.X == b.X && a.Y == b.Y;

    public int GetHashCode(Point2D p) => HashCode.Combine(p.X, p.Y);
}

var d = new Dictionary<Point2D, string>(new PointComparer());
d[new Point2D { X = 1, Y = 2 }] = "A";

Scénarios utiles

Dictionnaire de listes (accumulateur)

var map = new Dictionary<string, List<int>>();

void Add(string key, int value)
{
    if (!map.TryGetValue(key, out var list))
        map[key] = list = new List<int>();
    list.Add(value);
}

Add("alice", 1);
Add("alice", 2);
Add("bob", 9);

Dictionnaires imbriqués

var grid = new Dictionary<int, Dictionary<int, double>>();

void Set(int x, int y, double v)
{
    if (!grid.TryGetValue(x, out var inner))
        grid[x] = inner = new Dictionary<int, double>();
    inner[y] = v;
}

Regroupement avec LINQ (ToLookup / ToDictionary)

var people = new[]
{
    new { City = "Paris", Name = "Alice" },
    new { City = "Paris", Name = "Bob"   },
    new { City = "Lyon",  Name = "Claire"}
};

var byCity = people.ToLookup(p => p.City, p => p.Name);

var dict = people
    .GroupBy(p => p.City, p => p.Name)
    .ToDictionary(g => g.Key, g => g.ToList());

Vue en lecture seule

IReadOnlyDictionary<string, int> ro = d; // interface read-only

Alternatives

SortedDictionary (ordre trié, O(log n))

var sd = new SortedDictionary<string, int>(StringComparer.Ordinal);

ConcurrentDictionary (thread-safe)

var cd = new ConcurrentDictionary<string, int>(StringComparer.Ordinal);

int val = cd.GetOrAdd("alice", _ => 1);

int newVal = cd.AddOrUpdate(
    "alice",
    addValueFactory: _ => 1,
    updateValueFactory: (_, old) => old + 1);

Sérialisation JSON

System.Text.Json

var d = new Dictionary<string, int>
{
    ["a"] = 1,
    ["b"] = 2
};

string json = JsonSerializer.Serialize(d);
var d2 = JsonSerializer.Deserialize<Dictionary<string,int>>(json)!;

Recettes courantes

Incrémenter un compteur par clé

void Inc(Dictionary<string,int> counts, string key, int delta = 1)
{
    counts.TryGetValue(key, out int current);
    counts[key] = current + delta;
}

Fusionner deux dictionnaires (somme des valeurs)

var a = new Dictionary<string,int> { ["x"] = 1, ["y"] = 2 };
var b = new Dictionary<string,int> { ["y"] = 3, ["z"] = 4 };

foreach (var (k, v) in b)
{
    a[k] = a.TryGetValue(k, out int old) ? old + v : v;
}
// a = { x:1, y:5, z:4 }

Copier si absent (pattern TryAddOrGetExisting)

TValue GetOrAdd<TKey,TValue>(
    Dictionary<TKey,TValue> d, TKey key, Func<TValue> factory)
    where TKey : notnull
{
    if (!d.TryGetValue(key, out var val))
        d[key] = val = factory();
    return val;
}

Tri pour l'affichage

Itération triée via LINQ


var sorted = d.OrderBy(kv => kv.Key, StringComparer.Ordinal);
foreach (var (k, v) in sorted)
    Console.WriteLine($"{k}={v}");