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:
- Tüm elemanlar tek seferde grup anahtarlarına göre organize edilir
- Her anahtar için bir koleksiyon oluşturulur
- 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ı
- Null-safe: Var olmayan anahtarlar için boş koleksiyon döner
- Immutable: Oluşturulduktan sonra değiştirilemez
- Performanslı: O(1) erişim süresi
- LINQ entegrasyonu: ToLookup() ile kolay oluşturma
- Type-safe: Compile-time tip kontrolü
Dezavantajları
- Read-only: Oluşturulduktan sonra değiştirilemez
- Memory overhead: Tüm veriler bellekte tutulur
- 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.