TOC

This article has been localized into Czech by the community.

Sbírky:

Seznam

C# má řadu tříd pro práci se seznamy. Implementují rozhraní IList a nejpopulárnější implementací je generický seznam, často označovaný jako List<T>. T specifikuje typ objektů obsažených v seznamu, což má tu výhodu, že kompilátor zkontroluje a zajistí, že do seznamu přidáte pouze objekty správného typu - jinými slovy, List<T> je typově bezpečný.

List je velmi podobný třídě ArrayList, která byla před podporou generických seznamů v C# oblíbenou volbou pro seznamy. Proto také uvidíte, že List může dělat hodně stejných věcí jako pole (které mimochodem také implementuje rozhraní IList), ale ve spoustě situací je List jednodušší a snazší na práci. Například nemusíte vytvářet List s konkrétní velikostí - místo toho ho můžete prostě vytvořit a .NET ho automaticky rozšíří tak, aby odpovídal množství položek, které do něj přidáváte.

Jak bylo uvedeno, T znamená typ a slouží k zadání typu objektů, které chcete, aby obsahoval. V našem prvním příkladu vám ukážeme, jak vytvořit seznam, který by měl obsahovat řetězce:

List<string> listOfStrings = new List<string>();

Toto vytvoří prázdný seznam, ale přidání dalších položek je velmi snadné pomocí metody Add:

listOfStrings.Add("a string");

Pokud se ale pokusíte přidat něco, co není řetězec, kompilátor si začne okamžitě stěžovat na danou položku:

listOfStrings.Add(2);
Error   CS1503  Argument 1: cannot convert from 'int' to 'string'

Inicializace seznamu s položkami

V předchozím příkladu jsme prostě vytvořili seznam a poté do něj přidali položku. C# však umožňuje vytvořit seznam a zároveň do něj přidat položky, v rámci téhož příkazu, pomocí techniky nazývané inicializátory kolekcí. Podívejme se, jak se to dělá:

List<string> listOfNames = new List<string>()
{
    "John Doe",
    "Jane Doe",
    "Joe Doe"
};

Syntaxe je poměrně jednoduchá: Před obvyklým koncovým středníkem máme sadu složených závorek, které obsahují seznam hodnot, které chceme mít v seznamu od začátku. Jelikož je to seznam řetězců, počáteční objekty, které poskytujeme, by samozřejmě měly být typu string. Totéž však může být dosaženo i pro seznamy jiných typů, dokonce, i když používáme vlastní třídy, jak ukážu v dalším příkladu.

Práce s položkami v seznamu

Existuje několik způsobů, jak pracovat s položkami generického seznamu, a abych vám některé z nich ukázal, vytvořil jsem rozsáhlejší příklad:

using System;
using System.Collections.Generic;

namespace Lists
{
    class Program
    {
static void Main(string[] args)
{
    List<User> listOfUsers = new List<User>()
    {
new User() { Name = "John Doe", Age = 42 },
new User() { Name = "Jane Doe", Age = 34 },
new User() { Name = "Joe Doe", Age = 8 },
    };

    for(int i = 0; i < listOfUsers.Count; i++)
    {
Console.WriteLine(listOfUsers[i].Name + " is " + listOfUsers[i].Age + " years old");
    }
    Console.ReadKey();
}
    }

    class User
    {
public string Name { get; set; }

public int Age { get; set; }
    }
}

Začněme odspodu, kde definujeme jednoduchou třídu pro uchování informací o uživateli - jen jméno a věk. Vraťme se na začátek příkladu, kde jsem změnil náš seznam, aby místo jednoduchých řetězců používal tuto třídu User. Používám inicializátor kolekce k naplnění seznamu uživateli - všimněte si, že syntaxe je stejná jako předtím, jen trochu složitější, protože se zabýváme složitějším objektem, než je řetězec.

Jakmile máme seznam připraven, použiji smyčku for k procházení seznamu - abych věděl, kolik iterací provedeme, použiji vlastnost Count seznamu. Při každé iteraci přistupuji k danému uživateli prostřednictvím indexátoru seznamu s použitím syntaxe se složenými závorkami (např. listOfUsers[i]). Jakmile mám uživatele, vypíšu jméno a věk.

Přidávání, vkládání a odstraňování položek

Již jsme vyzkoušeli přidání jedné položky do seznamu, ale existují další možnosti, jak to udělat. Především můžete místo přidání vložit položku - rozdíl je v tom, že zatímco metoda Add vždy přidává na konec seznamu, metoda Insert umožňuje vložit položku na konkrétní pozici. Zde je příklad:

List<string> listOfNames = new List<string>()
{
    "Joe Doe"
};
// Insert at the top (index 0)
listOfNames.Insert(0, "John Doe");
// Insert in the middle (index 1)
listOfNames.Insert(1, "Jane Doe");

Začneme seznam s pouze jednou položkou, ale poté vložíme další dvě položky, nejprve na začátek seznamu a potom doprostřed. První parametr metody Insert je index, na kterém chceme položku vložit. Buďte opatrní - pokud se pokusíte vložit položku na index 3, když má seznam méně položek, dojde k vyhození výjimky!

Přidání více položek

Stejně jako máme metody Add a Insert pro přidání jedné položky, existují také odpovídající metody pro přidání a vložení více položek. Jmenují se AddRange() a InsertRange() a přijímají jakýkoli typ kolekce, která implementuje rozhraní IEnumerable jako parametr - to může být například pole položek nebo další seznam, jehož položky chcete přidat nebo vložit do aktuálního seznamu.

Jako příklad na metody pro práci s rozsahem udělejme něco zábavného - zkombinujeme metodu AddRange s inicializátorem kolekce, abychom do existujícího seznamu přidali několik nových jmen v jediném příkazu:

listOfNames.AddRange(new string[]
    {
"Jenna Doe",
"Another Doe"
    });

Jednoduše vytvoříme pole řetězců a okamžitě přidáme jeho položky do našeho seznamu jmen z předchozího příkladu.

Odstraňování položek

Když chcete ze seznamu odstranit jednu nebo více položek, máte k dispozici tři metody: Remove(), RemoveAt() a RemoveAll().

Metoda Remove() přijímá pouze jeden parametr: položku, kterou chcete odstranit. To je skvělé například pro seznam řetězců nebo čísel, protože můžete jednoduše napsat položku, kterou chcete odstranit. Na druhou stranu, pokud máte seznam složitých objektů, museli byste nejprve najít tento objekt, abyste měli odkaz, který byste mohli předat metodě Remove(). Zabývejme se tím později - zde je velmi základní příklad, jak můžete odstranit jednu položku metodou Remove():

List<string> listOfNames = new List<string>()
{
    "John Doe",
    "Jane Doe",
    "Joe Doe",
    "Another Doe"
};

listOfNames.Remove("Joe Doe");

Metoda Remove() prostě prochází seznam, dokud nenajde první instanci objektu, který jste určili k odstranění, a pak ji odstraní - odstraní pouze jednu instanci a pokud specifikujete položku v seznamu, která neexistuje, není vyvolána žádná chyba. Metoda vrací true, pokud se jí podařilo položku odstranit, a false, pokud ne.

Metoda RemoveAt() využívá faktu, že generický seznam je založen na indexech, tím, že vám umožňuje odstranit položku na základě jejího indexu/pozice v seznamu. Například můžete odstranit takto první položku ze seznamu:

listOfNames.RemoveAt(0);

Nebo takto poslední položku v seznamu:

listOfNames.RemoveAt(listOfNames.Count - 1);

I v tomto případě dochází k odstranění pouze jedné položky a tentokrát byste měli být opatrní při zadávání indexu položky, která má být odstraněna - pokud použijete index, který je mimo rozsah (nižší než 0 nebo vyšší než počet položek), bude vyvolána výjimka! Proto, pokud si nejste svým postupem jisti, můžete metodu RemoveAt() obalit do bloku try-catch pro zpracování výjimky (vysvětleno podrobněji jinde v tomto tutoriálu). Metoda RemoveAt() nevrací žádnou hodnotu, takže budete muset před a po volání zkontrolovat počet položek v seznamu, abyste rozhodli, zda bylo volání úspěšné - na druhou stranu, pokud víte, že máte index, který v seznamu existuje, což byste měli vždy zajistit, pak můžete vždy očekávat, že metoda RemoveAt() bude úspěšná.

Metoda RemoveAll() je nejsložitější z metod pro odstranění, ale rozhodně také nejvýkonnější. Jako parametr přijímá delegáta na metodu, a tato metoda pak rozhoduje, zda má být položka odstraněna, nebo ne, a to vrácením hodnoty true nebo false. To vám umožňuje aplikovat vlastní logiku při odstraňování položek a také vám to umožňuje odstranit více než jednu položku najednou. Delegáti budou ošetřeni jinde v tomto tutoriálu, protože jde o velké a složité téma, ale přesto chci, abyste pochopili, jak skvělá metoda RemoveAll je, takže zde je příklad:

List<string> listOfNames = new List<string>()
{
    "John Doe",
    "Jane Doe",
    "Joe Doe",
    "Another Doe"
};

listOfNames.RemoveAll(name =>
{
    if (name.StartsWith("J"))
return true;
    else
return false;
});

V tomto příkladu používáme anonymní metodu (opět příliš složitá na to, aby byla zde vysvětlena, ale bude ošetřena v jiné kapitole) jako parametr pro metodu RemoveAll. Naše anonymní metoda je poměrně jednoduchá - bude volána pro každou položku v seznamu a má parametr nazvaný name (jméno), který je samozřejmě aktuální položkou v iteraci. Podívá se na toto jméno, a pokud začíná na "J", vrací true - jinak false. Metoda RemoveAll() používá tuto odpověď (true nebo false) k rozhodnutí, zda má být každá položka odstraněna, či nikoli. Nakonec to zanechá v našem původním seznamu jen jednoho člena rodiny Doe: Another Doe.

Třídění položek v seznamu

Doposud byly položky v seznamu, se kterými jsme pracovali, používány v pořadí, ve kterém byly přidány do seznamu. Nicméně možná budete chtít mít položky seřazené určitým způsobem, například abecedně v případě našeho seznamu jmen. List<T> má metodu Sort(), kterou můžeme pro toto použít:

List<string> listOfNames = new List<string>()
{
    "John Doe",
    "Jane Doe",
    "Joe Doe",
    "Another Doe"
};
listOfNames.Sort();
foreach (string name in listOfNames)
    Console.WriteLine(name);

Jak uvidíte z výstupu, položky v seznamu jsou nyní seřazené abecedně, a pokud je chcete místo toho v sestupném pořadí (od Z k A), jednoduše zavolejte metodu Reverse() po provedení seřazení:

listOfNames.Sort();
listOfNames.Reverse();

Seřadit seznam bylo tedy docela snadné, že? No, bylo to hlavně tak snadné, protože máme seznam řetězců a .NET framework přesně ví, jak porovnat dva řetězce. Pokud máte seznam čísel, .NET samozřejmě také bude vědět, jak to seřadit. Na druhou stranu, můžete mít seznam vlastních objektů (protože List<T> může obsahovat jakýkoliv objekt), které .NET nemá šanci vědět, jak porovnat. Existuje několik řešení tohoto problému, např. implementace rozhraní IComparable nebo použití LINQ (na oba způsoby se podíváme později v tomto tutoriálu), ale jako rychlé řešení můžeme také jednoduše poskytnout metodě Sort() volání, aby se naučila, jak se dvě položky staví proti sobě, takto:

using System;
using System.Collections.Generic;

namespace ListSort
{
    class Program
    {
static void Main(string[] args)
{
    List<User> listOfUsers = new List<User>()
    {
new User() { Name = "John Doe", Age = 42 },
new User() { Name = "Jane Doe", Age = 39 },
new User() { Name = "Joe Doe", Age = 13 },
    };
    listOfUsers.Sort(CompareUsers);
    foreach (User user in listOfUsers)
Console.WriteLine(user.Name + ": " + user.Age + " years old");
}

public static int CompareUsers(User user1, User user2)
{
    return user1.Age.CompareTo(user2.Age);
}
    }

    public class User
    {
public string Name { get; set; }
public int Age { get; set; }
    }
}

To přidalo do našeho příkladu docela dost kódu, ale ve skutečnosti to není příliš složité. Pokud začneme odspodu, vytvořil jsem velmi jednoduchou třídu User, skládající se ze jména a věku. Uprostřed jsem deklaroval metodu nazvanou CompareUsers() - bere dva uživatele jako parametry a poté vrací celé číslo, které bude indikovat, zda je jedna položka "menší", "stejná" nebo "větší" (-1, 0 nebo 1). Tyto hodnoty budou využity metodou Sort() k přesunutí položek tak, aby pořadí položek odpovídalo tomu, co chceme. V tomto případě jednoduše používám vlastnost Age pro porovnání, což nám v podstatě zanechá seznam uživatelů seřazených podle jejich věku.

Shrnutí

Tento článek je jedním z těch delších v tomto tutoriálu, ale doufáme, že jste se dozvěděli hodně o seznamech, protože čím více programujete, tím více si uvědomíte, jak důležité jsou seznamy a slovníky (dictionary). Když hovoříme o slovnících, budeme o nich diskutovat v dalším článku.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!