.NET’te ILookup Interface Kullanımı

.NET’te ILookup Interface Kullanımı

ILookup Nedir?

ILookup<TKey, TElement>, .NET Framework 3.5 ile birlikte (2007) tanıtılan bir arayüzdür. Birden fazla değeri tek bir anahtarla eşleştirmenizi sağlayan, salt okunur bir koleksiyon yapısıdır. Dictionary<TKey, List<TValue>> yapısına benzer ancak daha optimize edilmiş ve kullanımı daha kolaydır.

Hangi Sorunu Çözer?

Geleneksel Dictionary yapısında her anahtar yalnızca tek bir değere sahip olabilir. Gerçek hayatta ise bir anahtara birden fazla değer atamamız gereken durumlar vardır:

  • Bir kategorideki ürünler
  • Bir departmandaki çalışanlar
  • Bir şehirdeki müşteriler

Bu durumları Dictionary<string, List<T>> ile çözebilirsiniz ancak bu yaklaşım hem zahmetli hem de hata yapmaya açıktır.

ILookup Nasıl Bu Sorunu Çözer?

ILookup, grup bazlı sorgulama yapmanızı kolaylaştırır ve performanslı bir şekilde key-value çiftlerini yönetir. LINQ’in ToLookup() metodu ile kolayca oluşturulabilir.

Kod Örnekleri

Temel Kullanım




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

public class Employee
{
    public string Name { get; set; }
    public string Department { get; set; }
    public int Salary { get; set; }
}

class Program
{
    static void Main()
    {
        var employees = new List<Employee>
        {
            new Employee { Name = "John", Department = "IT", Salary = 60000 },
            new Employee { Name = "Jane", Department = "HR", Salary = 55000 },
            new Employee { Name = "Bob", Department = "IT", Salary = 65000 },
            new Employee { Name = "Alice", Department = "HR", Salary = 58000 },
            new Employee { Name = "Charlie", Department = "IT", Salary = 70000 }
        };

        // Create ILookup
        ILookup<string, Employee> employeesByDept = 
            employees.ToLookup(e => e.Department);

        // Query employees in IT department
        foreach (var emp in employeesByDept["IT"])
        {
            Console.WriteLine($"{emp.Name} - ${emp.Salary}");
        }
        // Output:
        // John - $60000
        // Bob - $65000
        // Charlie - $70000

        // Check if department exists
        if (employeesByDept.Contains("Finance"))
        {
            Console.WriteLine("Finance department exists");
        }
        else
        {
            Console.WriteLine("Finance department not found");
        }
        // Output: Finance department not found

        // Count employees per department
        Console.WriteLine($"IT Department: {employeesByDept["IT"].Count()} employees");
        Console.WriteLine($"HR Department: {employeesByDept["HR"].Count()} employees");
        // Output:
        // IT Department: 3 employees
        // HR Department: 2 employees
    }
}

//Kompleks Anahtar ile Kullanım

public class Order
{
    public int OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public string CustomerId { get; set; }
    public decimal Amount { get; set; }
}

class ComplexKeyExample
{
    static void Main()
    {
        var orders = new List<Order>
        {
            new Order { OrderId = 1, OrderDate = new DateTime(2024, 1, 15), 
                       CustomerId = "CUST001", Amount = 100 },
            new Order { OrderId = 2, OrderDate = new DateTime(2024, 1, 20), 
                       CustomerId = "CUST001", Amount = 150 },
            new Order { OrderId = 3, OrderDate = new DateTime(2024, 2, 10), 
                       CustomerId = "CUST002", Amount = 200 },
            new Order { OrderId = 4, OrderDate = new DateTime(2024, 2, 15), 
                       CustomerId = "CUST001", Amount = 175 }
        };

        // Group by Year-Month
        var ordersByMonth = orders.ToLookup(
            o => new { Year = o.OrderDate.Year, Month = o.OrderDate.Month }
        );

        // Query orders for January 2024
        var janOrders = ordersByMonth[new { Year = 2024, Month = 1 }];
        Console.WriteLine($"January 2024 Orders: {janOrders.Count()}");
        Console.WriteLine($"Total Amount: ${janOrders.Sum(o => o.Amount)}");
        // Output:
        // January 2024 Orders: 2
        // Total Amount: $250
    }
}

Performans Karşılaştırması

class PerformanceComparison
{
    static void Main()
    {
        var products = GenerateProducts(10000);
        var sw = new System.Diagnostics.Stopwatch();

        // Using Dictionary<string, List<Product>>
        sw.Start();
        var dict = new Dictionary<string, List<Product>>();
        foreach (var product in products)
        {
            if (!dict.ContainsKey(product.Category))
                dict[product.Category] = new List<Product>();
            dict[product.Category].Add(product);
        }
        sw.Stop();
        Console.WriteLine($"Dictionary approach: {sw.ElapsedMilliseconds}ms");

        // Using ILookup
        sw.Restart();
        var lookup = products.ToLookup(p => p.Category);
        sw.Stop();
        Console.WriteLine($"ILookup approach: {sw.ElapsedMilliseconds}ms");

        // Query performance
        sw.Restart();
        var electronicsDict = dict.ContainsKey("Electronics") 
            ? dict["Electronics"] 
            : new List<Product>();
        sw.Stop();
        Console.WriteLine($"Dictionary query: {sw.ElapsedTicks} ticks");

        sw.Restart();
        var electronicsLookup = lookup["Electronics"];
        sw.Stop();
        Console.WriteLine($"ILookup query: {sw.ElapsedTicks} ticks");
    }

    static List<Product> GenerateProducts(int count)
    {
        var categories = new[] { "Electronics", "Books", "Clothing", "Food" };
        var random = new Random();
        var products = new List<Product>();
        
        for (int i = 0; i < count; i++)
        {
            products.Add(new Product
            {
                Id = i,
                Name = $"Product {i}",
                Category = categories[random.Next(categories.Length)],
                Price = random.Next(10, 1000)
            });
        }
        return products;
    }
}

class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}

Arka Planda Nasıl Çalışır?

ILookup dahili olarak hash tablosu kullanır. ToLookup() metodu çağrıldığında:

  1. Tüm elemanlar tek seferde grup anahtarlarına göre organize edilir
  2. Her anahtar için bir koleksiyon oluşturulur
  3. Sonuç immutable (değiştirilemez) bir yapıda saklanır

Alternatifler ve Karşılaştırma

1. Dictionary<TKey, List<TValue>>




// Manual grouping with Dictionary
var dict = new Dictionary<string, List<Employee>>();
foreach (var emp in employees)
{
    if (!dict.ContainsKey(emp.Department))
        dict[emp.Department] = new List<Employee>();
    dict[emp.Department].Add(emp);
}

2. GroupBy




// LINQ GroupBy - creates IGrouping
var groups = employees.GroupBy(e => e.Department);
foreach (var group in groups)
{
    Console.WriteLine($"Department: {group.Key}");
    foreach (var emp in group)
        Console.WriteLine($"  {emp.Name}");
}

3. MultiValueDictionary (System.Collections.Specialized)




// Requires additional NuGet package
var mvd = new MultiValueDictionary<string, Employee>();
foreach (var emp in employees)
{
    mvd.Add(emp.Department, emp);
}

ILookup’ın Avantajları

  1. Null-safe: Var olmayan anahtarlar için boş koleksiyon döner
  2. Immutable: Oluşturulduktan sonra değiştirilemez
  3. Performanslı: O(1) erişim süresi
  4. LINQ entegrasyonu: ToLookup() ile kolay oluşturma
  5. Type-safe: Compile-time tip kontrolü

Dezavantajları

  1. Read-only: Oluşturulduktan sonra değiştirilemez
  2. Memory overhead: Tüm veriler bellekte tutulur
  3. Yeniden oluşturma: Değişiklik için komple yeniden oluşturulmalı

Sonuç

ILookup, bir anahtara birden fazla değer atanması gereken senaryolarda güçlü ve kullanımı kolay bir çözümdür. Özellikle veri gruplandırma, kategorileme ve hızlı sorgulama gerektiren durumlarda Dictionary alternatiflerine göre daha temiz ve performanslı kod yazmanızı sağlar.

Yorum bırakın