Kolekce (C#)

U mnoha aplikací chcete vytvořit a spravovat skupiny souvisejících objektů. Objekty můžete seskupit dvěma způsoby: vytvořením polí objektů a vytvořením kolekcí objektů.

Pole jsou nejužitečnější pro vytváření a práci s pevným počtem objektů silného typu. Informace o polích najdete v tématu Pole.

Kolekce poskytují flexibilnější způsob práce se skupinami objektů. Na rozdíl od polí může skupina objektů, se kterými pracujete, dynamicky zvětšovat a zmenšit podle potřeb aplikace. U některých kolekcí můžete přiřadit klíč k libovolnému objektu, který vložíte do kolekce, abyste mohli objekt rychle načíst pomocí klíče.

Kolekce je třída, takže musíte deklarovat instanci třídy, abyste mohli do této kolekce přidat prvky.

Pokud kolekce obsahuje prvky pouze jednoho datového typu, můžete použít jednu z tříd v System.Collections.Generic oboru názvů. Obecná kolekce vynucuje bezpečnost typů, aby do ní nebyl přidán žádný jiný datový typ. Když načtete prvek z obecné kolekce, nemusíte určit jeho datový typ nebo ho převést.

Poznámka

Příklady v tomto tématu obsahují direktivy using pro System.Collections.Generic obory názvů a System.Linq obory názvů.

V tomto tématu

Používání jednoduché kolekce

Příklady v této části používají obecnou List<T> třídu, která umožňuje pracovat se seznamem objektů se silným typem.

Následující příklad vytvoří seznam řetězců a pak iteruje řetězce pomocí příkazu foreach .

// Create a list of strings.
var salmons = new List<string>();
salmons.Add("chinook");
salmons.Add("coho");
salmons.Add("pink");
salmons.Add("sockeye");

// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook coho pink sockeye

Pokud je obsah kolekce předem známý, můžete inicializovat kolekci pomocí inicializátoru kolekce . Další informace najdete v tématu Inicializátory objektů a kolekcí.

Následující příklad je stejný jako v předchozím příkladu, s výjimkou inicializátoru kolekce se používá k přidání elementů do kolekce.

// Create a list of strings by using a
// collection initializer.
var salmons = new List<string> { "chinook", "coho", "pink", "sockeye" };

// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook coho pink sockeye

Příkaz for můžete použít místo foreach příkazu k iteraci kolekce. Dosáhnete toho tak, že k prvkům kolekce přistupujete podle pozice indexu. Index prvků začíná na 0 a končí na počtu prvků minus 1.

Následující příklad iteruje prvky kolekce pomocí místo forforeach.

// Create a list of strings by using a
// collection initializer.
var salmons = new List<string> { "chinook", "coho", "pink", "sockeye" };

for (var index = 0; index < salmons.Count; index++)
{
    Console.Write(salmons[index] + " ");
}
// Output: chinook coho pink sockeye

Následující příklad odebere prvek z kolekce zadáním objektu, který chcete odebrat.

// Create a list of strings by using a
// collection initializer.
var salmons = new List<string> { "chinook", "coho", "pink", "sockeye" };

// Remove an element from the list by specifying
// the object.
salmons.Remove("coho");

// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook pink sockeye

Následující příklad odebere prvky z obecného seznamu. foreach Místo příkazu se použije příkaz, for který iteruje sestupně. Důvodem je to, RemoveAt že metoda způsobí, že prvky po odebrání elementu mají nižší hodnotu indexu.

var numbers = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// Remove odd numbers.
for (var index = numbers.Count - 1; index >= 0; index--)
{
    if (numbers[index] % 2 == 1)
    {
        // Remove the element by specifying
        // the zero-based index in the list.
        numbers.RemoveAt(index);
    }
}

// Iterate through the list.
// A lambda expression is placed in the ForEach method
// of the List(T) object.
numbers.ForEach(
    number => Console.Write(number + " "));
// Output: 0 2 4 6 8

Pro typ prvků v sadě List<T>můžete také definovat vlastní třídu. V následujícím příkladu Galaxy je třída, která je používána List<T> v kódu.

private static void IterateThroughList()
{
    var theGalaxies = new List<Galaxy>
        {
            new Galaxy() { Name="Tadpole", MegaLightYears=400},
            new Galaxy() { Name="Pinwheel", MegaLightYears=25},
            new Galaxy() { Name="Milky Way", MegaLightYears=0},
            new Galaxy() { Name="Andromeda", MegaLightYears=3}
        };

    foreach (Galaxy theGalaxy in theGalaxies)
    {
        Console.WriteLine(theGalaxy.Name + "  " + theGalaxy.MegaLightYears);
    }

    // Output:
    //  Tadpole  400
    //  Pinwheel  25
    //  Milky Way  0
    //  Andromeda  3
}

public class Galaxy
{
    public string Name { get; set; }
    public int MegaLightYears { get; set; }
}

Typy kolekcí

Rozhraní .NET poskytuje řadu běžných kolekcí. Každý typ kolekce je určený pro konkrétní účel.

Některé běžné třídy kolekcí jsou popsány v této části:

Třídy System.Collections.Generic

Obecnou kolekci můžete vytvořit pomocí jedné z tříd v System.Collections.Generic oboru názvů. Obecná kolekce je užitečná, když každá položka v kolekci má stejný datový typ. Obecná kolekce vynucuje silné psaní tím, že povolí přidání pouze požadovaného datového typu.

Následující tabulka uvádí některé často používané třídy System.Collections.Generic oboru názvů:

Třída Popis
Dictionary<TKey,TValue> Představuje kolekci párů klíč/hodnota, které jsou uspořádané na základě klíče.
List<T> Představuje seznam objektů, ke kterým je možné získat přístup pomocí indexu. Poskytuje metody pro vyhledávání, řazení a úpravy seznamů.
Queue<T> Představuje první kolekci objektů (FIFO).
SortedList<TKey,TValue> Představuje kolekci párů klíč/hodnota, které jsou seřazené podle klíče na základě přidružené IComparer<T> implementace.
Stack<T> Představuje poslední kolekci objektů (LIFO).

Další informace naleznete v tématu Běžně používané typy kolekcí, výběr třídy kolekce a System.Collections.Generic.

Třídy System.Collections.Concurrent

V .NET Framework 4 a novějších verzích poskytují kolekce v System.Collections.Concurrent oboru názvů efektivní operace bezpečné pro přístup k položkám kolekce z více vláken.

Třídy v System.Collections.Concurrent oboru názvů by se měly používat místo odpovídajících typů v System.Collections.Generic oboru názvů a System.Collections vždy, když ke kolekci současně přistupuje více vláken. Další informace najdete v tématu Kolekce vláken Sejf a System.Collections.Concurrent.

Některé třídy zahrnuté v System.Collections.Concurrent oboru názvů jsou BlockingCollection<T>, ConcurrentDictionary<TKey,TValue>, a ConcurrentStack<T>ConcurrentQueue<T>.

Třídy System.Collections

Třídy v System.Collections oboru názvů neukládají elementy jako speciálně zadané objekty, ale jako objekty typu Object.

Kdykoli je to možné, měli byste místo starších typů v System.Collections oboru názvů použít obecné kolekce v System.Collections.Generic oboru názvů nebo System.Collections.Concurrent obor názvů.

Následující tabulka uvádí některé často používané třídy v System.Collections oboru názvů:

Třída Popis
ArrayList Představuje pole objektů, jejichž velikost se dynamicky zvyšuje podle potřeby.
Hashtable Představuje kolekci párů klíč/hodnota, které jsou uspořádané na základě kódu hash klíče.
Queue Představuje první kolekci objektů (FIFO).
Stack Představuje poslední kolekci objektů (LIFO).

Obor System.Collections.Specialized názvů poskytuje specializované a silně typované třídy kolekcí, jako jsou kolekce jen pro řetězce a propojené seznamy a hybridní slovníky.

Implementace kolekce párů klíč/hodnota

Obecná Dictionary<TKey,TValue> kolekce umožňuje přístup k prvkům v kolekci pomocí klíče každého prvku. Každý doplněk slovníku se skládá z hodnoty a jejího přidruženého klíče. Načtení hodnoty pomocí jeho klíče je rychlé, protože Dictionary třída je implementována jako hash tabulka.

Následující příklad vytvoří Dictionary kolekci a iteruje prostřednictvím slovníku foreach pomocí příkazu.

private static void IterateThruDictionary()
{
    Dictionary<string, Element> elements = BuildDictionary();

    foreach (KeyValuePair<string, Element> kvp in elements)
    {
        Element theElement = kvp.Value;

        Console.WriteLine("key: " + kvp.Key);
        Console.WriteLine("values: " + theElement.Symbol + " " +
            theElement.Name + " " + theElement.AtomicNumber);
    }
}

private static Dictionary<string, Element> BuildDictionary()
{
    var elements = new Dictionary<string, Element>();

    AddToDictionary(elements, "K", "Potassium", 19);
    AddToDictionary(elements, "Ca", "Calcium", 20);
    AddToDictionary(elements, "Sc", "Scandium", 21);
    AddToDictionary(elements, "Ti", "Titanium", 22);

    return elements;
}

private static void AddToDictionary(Dictionary<string, Element> elements,
    string symbol, string name, int atomicNumber)
{
    Element theElement = new Element();

    theElement.Symbol = symbol;
    theElement.Name = name;
    theElement.AtomicNumber = atomicNumber;

    elements.Add(key: theElement.Symbol, value: theElement);
}

public class Element
{
    public string Symbol { get; set; }
    public string Name { get; set; }
    public int AtomicNumber { get; set; }
}

Chcete-li místo toho použít inicializátor kolekce k sestavení Dictionary kolekce, můžete nahradit BuildDictionary a AddToDictionary metody následující metodou.

private static Dictionary<string, Element> BuildDictionary2()
{
    return new Dictionary<string, Element>
    {
        {"K",
            new Element() { Symbol="K", Name="Potassium", AtomicNumber=19}},
        {"Ca",
            new Element() { Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        {"Sc",
            new Element() { Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        {"Ti",
            new Element() { Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };
}

Následující příklad používá metodu ContainsKey a Item[] vlastnost Dictionary rychlého vyhledání položky podle klíče. Tato Item vlastnost umožňuje přístup k položce v elements kolekci pomocí jazyka elements[symbol] C#.

private static void FindInDictionary(string symbol)
{
    Dictionary<string, Element> elements = BuildDictionary();

    if (elements.ContainsKey(symbol) == false)
    {
        Console.WriteLine(symbol + " not found");
    }
    else
    {
        Element theElement = elements[symbol];
        Console.WriteLine("found: " + theElement.Name);
    }
}

Následující příklad místo toho používá metodu TryGetValue rychle najít položku podle klíče.

private static void FindInDictionary2(string symbol)
{
    Dictionary<string, Element> elements = BuildDictionary();

    Element theElement = null;
    if (elements.TryGetValue(symbol, out theElement) == false)
        Console.WriteLine(symbol + " not found");
    else
        Console.WriteLine("found: " + theElement.Name);
}

Přístup ke kolekci pomocí jazyka LINQ

LinQ (jazykově integrovaný dotaz) se dá použít pro přístup k kolekcím. Dotazy LINQ poskytují možnosti filtrování, řazení a seskupení. Další informace najdete v tématu Začínáme s LINQ v jazyce C#.

Následující příklad spustí dotaz LINQ na obecný List. Dotaz LINQ vrátí jinou kolekci obsahující výsledky.

private static void ShowLINQ()
{
    List<Element> elements = BuildList();

    // LINQ Query.
    var subset = from theElement in elements
                 where theElement.AtomicNumber < 22
                 orderby theElement.Name
                 select theElement;

    foreach (Element theElement in subset)
    {
        Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
    }

    // Output:
    //  Calcium 20
    //  Potassium 19
    //  Scandium 21
}

private static List<Element> BuildList()
{
    return new List<Element>
    {
        { new Element() { Symbol="K", Name="Potassium", AtomicNumber=19}},
        { new Element() { Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        { new Element() { Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        { new Element() { Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };
}

public class Element
{
    public string Symbol { get; set; }
    public string Name { get; set; }
    public int AtomicNumber { get; set; }
}

Řazení kolekce

Následující příklad znázorňuje postup řazení kolekce. Příklad seřadí instance třídy, které jsou uloženy Car v souboru List<T>. Třída Car implementuje IComparable<T> rozhraní, které vyžaduje, aby CompareTo byla implementovaná metoda.

Každé volání CompareTo metody provede jedno porovnání, které se používá k řazení. Uživatelem psaný kód v CompareTo metodě vrátí hodnotu pro každé porovnání aktuálního objektu s jiným objektem. Vrácená hodnota je menší než nula, pokud je aktuální objekt menší než druhý objekt, větší než nula, pokud je aktuální objekt větší než druhý objekt, a nula, pokud jsou rovny. To vám umožní definovat v kódu kritéria pro větší než, menší než a rovno.

ListCars Příkaz v metodě cars.Sort() seřadí seznam. Toto volání metody SortList<T> způsobí, CompareTo že metoda bude volána automaticky pro Car objekty v .List

private static void ListCars()
{
    var cars = new List<Car>
    {
        { new Car() { Name = "car1", Color = "blue", Speed = 20}},
        { new Car() { Name = "car2", Color = "red", Speed = 50}},
        { new Car() { Name = "car3", Color = "green", Speed = 10}},
        { new Car() { Name = "car4", Color = "blue", Speed = 50}},
        { new Car() { Name = "car5", Color = "blue", Speed = 30}},
        { new Car() { Name = "car6", Color = "red", Speed = 60}},
        { new Car() { Name = "car7", Color = "green", Speed = 50}}
    };

    // Sort the cars by color alphabetically, and then by speed
    // in descending order.
    cars.Sort();

    // View all of the cars.
    foreach (Car thisCar in cars)
    {
        Console.Write(thisCar.Color.PadRight(5) + " ");
        Console.Write(thisCar.Speed.ToString() + " ");
        Console.Write(thisCar.Name);
        Console.WriteLine();
    }

    // Output:
    //  blue  50 car4
    //  blue  30 car5
    //  blue  20 car1
    //  green 50 car7
    //  green 10 car3
    //  red   60 car6
    //  red   50 car2
}

public class Car : IComparable<Car>
{
    public string Name { get; set; }
    public int Speed { get; set; }
    public string Color { get; set; }

    public int CompareTo(Car other)
    {
        // A call to this method makes a single comparison that is
        // used for sorting.

        // Determine the relative order of the objects being compared.
        // Sort by color alphabetically, and then by speed in
        // descending order.

        // Compare the colors.
        int compare;
        compare = String.Compare(this.Color, other.Color, true);

        // If the colors are the same, compare the speeds.
        if (compare == 0)
        {
            compare = this.Speed.CompareTo(other.Speed);

            // Use descending order for speed.
            compare = -compare;
        }

        return compare;
    }
}

Definice vlastní kolekce

Kolekci můžete definovat implementací IEnumerable<T> nebo IEnumerable rozhraním.

I když můžete definovat vlastní kolekci, je obvykle lepší použít kolekce, které jsou součástí .NET, které jsou popsány v části Druhy kolekcí dříve v tomto článku.

Následující příklad definuje vlastní třídu kolekce s názvem AllColors. Tato třída implementuje IEnumerable rozhraní, které vyžaduje, aby GetEnumerator byla metoda implementována.

Metoda GetEnumerator vrátí instanci ColorEnumerator třídy. ColorEnumerator implementuje IEnumerator rozhraní, které vyžaduje implementaci Current vlastnosti, MoveNext metody a Reset metody.

private static void ListColors()
{
    var colors = new AllColors();

    foreach (Color theColor in colors)
    {
        Console.Write(theColor.Name + " ");
    }
    Console.WriteLine();
    // Output: red blue green
}

// Collection class.
public class AllColors : System.Collections.IEnumerable
{
    Color[] _colors =
    {
        new Color() { Name = "red" },
        new Color() { Name = "blue" },
        new Color() { Name = "green" }
    };

    public System.Collections.IEnumerator GetEnumerator()
    {
        return new ColorEnumerator(_colors);

        // Instead of creating a custom enumerator, you could
        // use the GetEnumerator of the array.
        //return _colors.GetEnumerator();
    }

    // Custom enumerator.
    private class ColorEnumerator : System.Collections.IEnumerator
    {
        private Color[] _colors;
        private int _position = -1;

        public ColorEnumerator(Color[] colors)
        {
            _colors = colors;
        }

        object System.Collections.IEnumerator.Current
        {
            get
            {
                return _colors[_position];
            }
        }

        bool System.Collections.IEnumerator.MoveNext()
        {
            _position++;
            return (_position < _colors.Length);
        }

        void System.Collections.IEnumerator.Reset()
        {
            _position = -1;
        }
    }
}

// Element class.
public class Color
{
    public string Name { get; set; }
}

Iterátory

Iterátor se používá k provedení vlastní iterace v kolekci. Iterátor může být metoda nebo get příslušenství. Iterátor používá příkaz yield return k vrácení každého prvku kolekce jeden po druhém.

Iterátor můžete volat pomocí příkazu foreach . Každá iterace smyčky foreach volá iterátor. yield return Při dosažení příkazu v iterátoru se vrátí výraz a aktuální umístění v kódu se zachová. Spuštění se restartuje z daného umístění při příštím volání iterátoru.

Další informace najdete v tématu Iterátory (C#).

Následující příklad používá metodu iterátoru. Metoda iterátoru obsahuje yield return příkaz, který je uvnitř smyčky for . ListEvenNumbers V metodě každá iterace foreach textu příkazu vytvoří volání metody iterátoru, která pokračuje k dalšímu yield return příkazu.

private static void ListEvenNumbers()
{
    foreach (int number in EvenSequence(5, 18))
    {
        Console.Write(number.ToString() + " ");
    }
    Console.WriteLine();
    // Output: 6 8 10 12 14 16 18
}

private static IEnumerable<int> EvenSequence(
    int firstNumber, int lastNumber)
{
    // Yield even numbers in the range.
    for (var number = firstNumber; number <= lastNumber; number++)
    {
        if (number % 2 == 0)
        {
            yield return number;
        }
    }
}

Viz také