Dictionary

</details>

Beskrivning

En Dictionary är en datastruktur som tillåter oss att lagra och hämta värden baserat på nycklar. Den fungerar på ett liknande sätt som en telefonbok där vi kan slå upp ett namn (nyckel) för att få fram ett telefonnummer (värde). Dictionary är en kraftfull datastruktur inom programmering som erbjuder snabb åtkomst och effektiva sökningar.

🎯 Efter denna artikel kommer du att:

  • Förstå Key-Value par - nyckel pekar på värde, unika nycklar 🗝️
  • Använda Dictionary<K,V> - för snabb uppslagning och mappning 📖
  • Optimera prestanda - O(1) lookup istället för O(n) sökning 🚀
  • Arbeta säkert - TryGetValue, ContainsKey, och undvika KeyNotFoundException 🛡️

🔧 Dictionary<K,V> Properties och Methods

Basic Properties och Indexing

Dictionary<string, int> scores = new Dictionary<string, int>();

// Properties
Console.WriteLine($"Count: {scores.Count}");           // 0
Console.WriteLine($"Keys: {scores.Keys.Count}");       // 0
Console.WriteLine($"Values: {scores.Values.Count}");   // 0

// Indexer - lägg till och läsa värden
scores["Anna"] = 95;        // Lägg till ny
scores["Bert"] = 87;        // Lägg till ny
scores["Anna"] = 98;        // Uppdatera befintlig

int annaScore = scores["Anna"];  // 98
// int missing = scores["Cilla"]; // KeyNotFoundException!

Säker åtkomst till värden

Dictionary<string, string> phoneBook = new Dictionary<string, string>
{
    {"Anna Andersson", "070-1234567"},
    {"Bert Bertsson", "070-2345678"},
    {"Cilla Cillason", "070-3456789"}
};

// TryGetValue - säkert sätt att hämta värde
if (phoneBook.TryGetValue("Anna Andersson", out string phone))
{
    Console.WriteLine($"Anna: {phone}");           // Anna: 070-1234567
}
else
{
    Console.WriteLine("Anna hittades inte");
}

// ContainsKey - kontrollera om nyckel finns
if (phoneBook.ContainsKey("David Davidsson"))
{
    string davidPhone = phoneBook["David Davidsson"];
}
else
{
    Console.WriteLine("David finns inte i telefonboken");
}

// ContainsValue - kontrollera om värde finns (O(n) operation!)
bool hasPhone = phoneBook.ContainsValue("070-1234567");  // true

Lägg till och ta bort element

Dictionary<int, string> products = new Dictionary<int, string>();

// Add - lägg till (throws exception om key redan finns!)
products.Add(1001, "Laptop");
products.Add(1002, "Mouse");
// products.Add(1001, "Keyboard"); // ArgumentException!

// Indexer - lägger till eller uppdaterar
products[1003] = "Monitor";     // Lägg till ny
products[1001] = "Gaming Laptop"; // Uppdatera befintlig

// TryAdd - säkert sätt att lägga till (.NET Core 2.0+)
bool added = products.TryAdd(1001, "Tablet");    // false - key finns redan
bool added2 = products.TryAdd(1004, "Tablet");   // true - lades till

// Remove - ta bort baserat på key (returnerar bool)
bool removed = products.Remove(1002);            // true - fanns och togs bort
bool notFound = products.Remove(9999);           // false - fanns inte

// Clear - ta bort alla element
// products.Clear();

Collections - Keys, Values

Dictionary<string, int> inventory = new Dictionary<string, int>
{
    {"Apples", 50},
    {"Bananas", 30},
    {"Oranges", 25},
    {"Grapes", 15}
};

// Keys collection - alla nycklar
Dictionary<string, int>.KeyCollection keys = inventory.Keys;
foreach (string product in keys)
{
    Console.WriteLine($"Produkt: {product}");
}

// Values collection - alla värden
Dictionary<string, int>.ValueCollection values = inventory.Values;
int totalItems = values.Sum();  // Requires System.Linq
Console.WriteLine($"Totalt antal: {totalItems}");

// Convert to arrays/lists
string[] productArray = inventory.Keys.ToArray();
List<int> quantityList = inventory.Values.ToList();

// Find products with low stock
var lowStock = inventory.Where(kvp => kvp.Value < 20)
                       .Select(kvp => kvp.Key)
                       .ToList();

Iteration - olika sätt att loopa

Dictionary<string, double> prices = new Dictionary<string, double>
{
    {"Bread", 25.50},
    {"Milk", 15.90},
    {"Eggs", 32.00}
};

// 1. KeyValuePair iteration
foreach (KeyValuePair<string, double> item in prices)
{
    Console.WriteLine($"{item.Key}: {item.Value:C}");
}

// 2. var iteration (samma som ovan, kortare)
foreach (var item in prices)
{
    Console.WriteLine($"{item.Key}: {item.Value:C}");
}

// 3. Deconstruction (.NET Core 2.0+)
foreach (var (product, price) in prices)
{
    Console.WriteLine($"{product}: {price:C}");
}

// 4. Keys only
foreach (string product in prices.Keys)
{
    double price = prices[product];
    Console.WriteLine($"{product}: {price:C}");
}

// 5. LINQ operations
var expensiveItems = prices.Where(p => p.Value > 20)
                          .OrderByDescending(p => p.Value)
                          .ToList();

Practical användningsområden

// 1. Caching - snabb lookup för beräknade värden
Dictionary<int, string> cache = new Dictionary<int, string>();

string ExpensiveOperation(int input)
{
    if (cache.TryGetValue(input, out string cachedResult))
    {
        Console.WriteLine("Cache hit!");
        return cachedResult;
    }

    // Simulera dyr operation
    string result = $"Result for {input}";
    cache[input] = result;
    Console.WriteLine("Cache miss - calculated and stored");
    return result;
}

// 2. Frequency counting
string text = "hello world hello";
Dictionary<string, int> wordCount = new Dictionary<string, int>();

foreach (string word in text.Split(' '))
{
    if (wordCount.TryGetValue(word, out int count))
    {
        wordCount[word] = count + 1;
    }
    else
    {
        wordCount[word] = 1;
    }

    // Alternativ - kortare (.NET 6+)
    // wordCount[word] = wordCount.TryGetValue(word, out int c) ? c + 1 : 1;
}

// 3. Lookup tables - mappning mellan värden
Dictionary<string, string> countryCapitals = new Dictionary<string, string>
{
    {"Sweden", "Stockholm"},
    {"Norway", "Oslo"},
    {"Denmark", "Copenhagen"},
    {"Finland", "Helsinki"}
};

string GetCapital(string country)
{
    return countryCapitals.TryGetValue(country, out string capital)
        ? capital
        : "Unknown";
}

// 4. Object indexing - snabb access till objekt
Dictionary<int, Customer> customers = new Dictionary<int, Customer>();

class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

void AddCustomer(Customer customer)
{
    customers[customer.Id] = customer;  // O(1) insertion
}

Customer FindCustomer(int id)
{
    return customers.TryGetValue(id, out Customer customer)
        ? customer
        : null;  // O(1) lookup
}

// 5. Configuration settings
Dictionary<string, string> settings = new Dictionary<string, string>
{
    {"DatabaseConnection", "Server=localhost;Database=MyApp"},
    {"ApiKey", "abc123def456"},
    {"Environment", "Development"}
};

string GetSetting(string key, string defaultValue = "")
{
    return settings.TryGetValue(key, out string value) ? value : defaultValue;
}

Performance och Best Practices

// ✅ Set initial capacity om du vet storlek
Dictionary<int, string> efficientDict = new Dictionary<int, string>(1000);

// ✅ Använd korrekt equality comparer för case-insensitive keys
Dictionary<string, int> caseInsensitive = new Dictionary<string, int>(
    StringComparer.OrdinalIgnoreCase);
caseInsensitive["Hello"] = 1;
caseInsensitive["HELLO"] = 2;  // Uppdaterar samma key
Console.WriteLine(caseInsensitive.Count); // 1

// ✅ Custom objects som keys behöver override Equals och GetHashCode
public class PersonKey
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override bool Equals(object obj)
    {
        return obj is PersonKey p &&
               FirstName == p.FirstName &&
               LastName == p.LastName;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(FirstName, LastName); // .NET Core 2.1+
    }
}

Dictionary<PersonKey, CustomerData> personDict = new Dictionary<PersonKey, CustomerData>();

// ❌ ContainsValue är O(n) - undvik om möjligt
bool hasValue = inventory.ContainsValue(50);  // Långsamt för stora dictionaries

// ✅ Håll reverse mapping om du ofta behöver value -> key lookup
Dictionary<string, int> nameToId = new Dictionary<string, int>();
Dictionary<int, string> idToName = new Dictionary<int, string>();

void AddMapping(string name, int id)
{
    nameToId[name] = id;
    idToName[id] = name;
}

Thread Safety

// Dictionary är INTE thread-safe
Dictionary<string, int> unsafeDict = new Dictionary<string, int>();

// ✅ Använd locks för thread safety
private readonly object lockObject = new object();

void ThreadSafeAdd(string key, int value)
{
    lock (lockObject)
    {
        unsafeDict[key] = value;
    }
}

// ✅ Eller använd ConcurrentDictionary
using System.Collections.Concurrent;

ConcurrentDictionary<string, int> threadSafeDict = new ConcurrentDictionary<string, int>();

// Thread-safe operations
threadSafeDict.TryAdd("key1", 100);
threadSafeDict.TryGetValue("key1", out int value);
threadSafeDict.TryUpdate("key1", 200, 100);  // Update only if current value is 100

Exempel med superhjältar

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // Skapa en Dictionary för att lagra hjältarnas namn och deras styrka
        Dictionary<string, int> heroStats = new Dictionary<string, int>
        {
            {"Superman", 100},
            {"Spider-Man", 85},
            {"Batman", 75},
            {"Wonder Woman", 95},
            {"Iron Man", 80}
        };

        // Säker åtkomst med TryGetValue
        if (heroStats.TryGetValue("Superman", out int supermanPower))
        {
            Console.WriteLine($"Superman har styrka: {supermanPower}");
        }

        // Lägg till ny hjälte
        heroStats["Thor"] = 98;

        // Hitta starkaste hjälten
        var strongest = heroStats.OrderByDescending(kvp => kvp.Value).First();
        Console.WriteLine($"Starkaste hjälten: {strongest.Key} ({strongest.Value})");

        // Räkna hjältar med hög styrka
        int powerfulHeroes = heroStats.Count(kvp => kvp.Value >= 90);
        Console.WriteLine($"Hjältar med styrka >= 90: {powerfulHeroes}");
    }
}

Detta är bara ett grundläggande exempel på hur man kan använda Dictionary i C-Sharp. Det finns mycket mer att utforska och lära sig om denna kraftfulla datastruktur.

Slutsats

Dictionary är en värdefull datastruktur som tillåter oss att lagra och hämta värden baserat på nycklar. Det erbjuder snabb åtkomst och effektiva sökningar, vilket gör det till en populär datastruktur inom programmering. Genom att använda Dictionary kan vi effektivt hantera och organisera data i våra program.

För att lära dig mer om Dictionary och hur det kan användas i C-Sharp-programmering, rekommenderas att utforska dokumentationen och exempelkod på Microsofts officiella webbplats eller andra resurser som erbjuder fördjupad information om ämnet.

TL;DR

En Dictionary är en datastruktur som låter oss lagra och hämta värden baserat på nycklar. Det ger snabb åtkomst och effektiva sökningar, vilket gör det till ett kraftfullt verktyg inom programmering.

Obligatorisk dad-joke

Varför älskar programmerare att använda dictionaries?

För att de alltid vill ha en “key” till framgång!


Upp

Upp


Licens: Apache 2.0 | © 2023 Marcus Medina, Campus Mölndal. Alla rättigheter förbehållna.
Du får använda och modifiera detta verk enligt villkoren i Apache License, Version 2.0. Du får inte använda detta verk för kommersiella ändamål utan tillstånd från upphovsmannen.