Lekce 12 - Kolekce Set, Queue a Map v Dartu
V minulé lekci, Datum a čas v Dartu, jsme si řekli něco o datu a času v Dartu. V dnešním tutoriálu si ukážeme další kolekce, které máme dostupné a s jejichž položkami můžeme pracovat jinak než-li v nám již známém seznamu.
Pojem kolekce jsme tu již zmínili. Je to struktura, do které můžeme
ukládat více objektů. Kolekcí je v Dartu několik, jsou uzpůsobeny pro
různé účely a můžeme s nimi zacházet různými způsoby. Dosud známe
pouze kolekci seznam (List
). V průběhu kurzu však budeme
potřebovat i další kolekce, které si dnes vysvětlíme.
Set
Set
je kolekce reprezentující množinu, která si je velmi
podobná s List
, avšak zásadním rozdílem je, že každý objekt
se v ní může vyskytovat pouze jednou, tj. neuchovává duplicity. Množinu
lze snadno převést na seznam a seznam na množinu pomocí metody
toSet()
či toList()
. Snadno tak lze z listu odstranit
duplicity pomocí zřetězení těchto metod, např.
mujList.toSet().toList()
.
Vyzkoušejme si ji:
Set<int> set = new Set(); set.add(1); set.add(1); set.add(2); set.add(1); print(set);
Výstup programu:
Konzolová aplikace
{1, 2}
Pro každý objekt zadaného typu lze určit, zda-li je nebo není v
množině. Pokud začneme množinu procházet, budou prvky v takovém pořadí,
v jakém byly přidány. Prvky se rozlišují podle operátoru ==
a
je doporučeno, pokud je tento operátor přepsán, taktéž přepsat i metodu
hashCode()
pro zachování konzistence.
Množina má taktéž oproti seznamu několik specifických funkcí:
union()
Metoda množinového sjednocení, která vrací novou množinu s prvky z první i z druhé množiny. Druhou množinu předáme parametrem. Nejprve jsou přidány prvky z první množiny v původním pořadí a následně jsou přidány zbývající prvky, které jsou ve druhé množině navíc, v pořadí z druhé množiny.
Set<int> a = new Set(); a.addAll([1, 2, 1, 1, 2, 4]); Set<int> b = new Set(); b.addAll([1, 2, 2, 2, 2, 3, 3, 5]); print(a); print(b); print(a.union(b)); print(b.union(a));
Výstup programu:
Konzolová aplikace
{1, 2, 4}
{1, 2, 3, 5}
{1, 2, 4, 3, 5}
{1, 2, 3, 5, 4}
difference()
Další zajímavou metodou je množinový rozdíl. Metoda vrátí novou množinu s prvky z první množiny, které se nevyskytovaly v druhé množině. Druhou množinu opět předáme parametrem.
Set<int> a = new Set(); a.addAll([1, 2, 1, 1, 2, 4]); Set<int> b = new Set(); b.addAll([1, 2, 2, 2, 2, 3, 3, 5]); print(a); print(b); print(a.difference(b)); print(b.difference(a));
Výstup programu:
Konzolová aplikace
{1, 2, 4}
{1, 2, 3, 5}
{4}
{3, 5}
intersection()
Poslední z množinových metod je množinový průnik. Metoda vrátí novou množinu obsahující pouze prvky, které byly společné v obou množinách.
Set<int> a = new Set(); a.addAll([1, 2, 1, 1, 2, 4]); Set<int> b = new Set(); b.addAll([1, 2, 2, 2, 2, 3, 3, 5]); print(a); print(b); print(a.intersection(b)); print(b.intersection(a));
Výstup programu:
Konzolová aplikace
{1, 2, 4}
{1, 2, 3, 5}
{1, 2}
{1, 2}
Vlastní objekt
Jak již bylo řečeno, pokud chceme pracovat s množinou, objekt musí mít
implementovaný operátor ==
a metodu hashCode()
. Pro
ukázku si vytvoříme objekt Auto
, které bude mít rychlost a
název. Následně vytvoříme 3 auta, přičemž 2 z nich budou mít stejné
hodnoty:
class Auto { int rychlost; String nazev; @override String toString() { return '$nazev($rychlost)'; } } void main() { Auto mazel = new Auto() ..nazev = 'Mazlík' ..rychlost = 150; Auto auto = new Auto() ..nazev = 'Mazlík' ..rychlost = 150; Auto popelnice = new Auto() ..nazev = 'Šunka' ..rychlost = 60; Set<Auto> auta = new Set(); auta.add(mazel); auta.add(auto); auta.add(popelnice); print(auta); }
Výstup programu:
Konzolová aplikace
{Mazlík(150), Mazlík(150), Šunka(60)}
Očekávané chování je samozřejmě takové, že když používáme
množinu, měly by v ní být ve výsledku jen 2 auta, protože uchovává pouze
unikátní objekty. Avšak hle – auto, které identifikujeme podle názvu a
rychlosti, se nám objevilo dvakrát. Zapomněli jsme implementovat operátor
==
a hashCode()
. Nemusíme vymýšlet relativně
složitý kód sami, IDE nám jej vygeneruje za nás. Stačí stisknout klávesy
ALT + INSERT (Generate...) a zvolit
==() and hashCode
.

My budeme chtít identifikovat naše auto podle názvu a rychlosti, proto zvolíme obě vlastnosti.
@override bool operator ==(Object other) => identical(this, other) || other is Auto && runtimeType == other.runtimeType && rychlost == other.rychlost && nazev == other.nazev; @override int get hashCode => rychlost.hashCode ^ nazev.hashCode;
Všimněte si novinky, klíčového slova operator
. Přesně tak
lze přepsat téměř libovolný operátor pro daný objekt. K přetěžování
operátorů se ještě vrátíme a vysvětlíme si vše více do hloubky. Znovu
si spustíme náš program, tentokrát již vše funguje přesně tak, jak
bychom očekávali:
Konzolová aplikace
{Mazlík(150), Šunka(60)}
Queue
Queue
je kolekce, která opět velmi připomíná seznam, avšak
reprezentuje frontu (FIFO) a zásobník (LIFO). To znamená, že je tato kolekce
optimalizovaná na přístup na první či poslední prvek. Pro používání je
nutné importovat knihovnu dart:collection
. Stejně jako seznam či
množina, i frontu lze procházet a bude fungovat i přístup na položku na
daném indexu, avšak kvůli implementaci fronty nebude přístup tak rychlý
jako v seznamech.
Vyzkoušíme si jednoduchou ukázku:
Queue<int> queue = new Queue(); queue.add(1); queue.add(2); queue.add(2); queue.add(3); print(queue);
Doposud je chování stejné se seznamem. Ukážeme si však další metody pro práci s frontou.
addLast()
Metoda pro vložení prvku na konec fronty. Lze nahradit metodou
add()
.
addFirst()
Metoda pro vložení prvku na začátek fronty.
Queue<int> queue = new Queue(); queue.add(1); queue.add(2); queue.add(2); queue.addFirst(3); print(queue);
Výstup programu:
Konzolová aplikace
{3, 1, 2, 2}
removeFirst()
a
removeLast()
Metody odstraní první/poslední prvek kolekce a tento prvek vrátí.
Queue<int> queue = new Queue(); queue.add(1); queue.add(2); queue.add(2); queue.addFirst(3); print(queue); print(queue.removeFirst()); print(queue); print(queue.removeLast()); print(queue);
Výstup programu:
Konzolová aplikace
{3, 1, 2, 2}
3
{1, 2, 2}
2
{1, 2}
Map
Mapa, asociativní pole, slovník, ... Všechny tyto názvy můžete znát z různých programovacích jazyků. V Dartu budeme používat pojem mapa. Mapa je kolekce, která uchovává své prvky pod zadanými klíči. V takové kolekci můžeme k prvkům přistupovat po zadání klíče. Stejně jako u předchozích kolekcí, prvky můžeme procházet v pořadí podle přidání. Datový typ klíče zapisujeme do lomených závorek na první místo a na druhé píšeme datový typ prvku. Ukažme si ukázku:
Map<int, String> mapa = new Map(); mapa[0] = 'nula'; mapa[1] = 'uno'; mapa[2] = 'два'; print(mapa);
Výstup programu:
Konzolová aplikace
{0: nula, 1: uno, 2: два}
Jak jste si jistě všimli již z ukázky, mapa nemá metodu
add()
. Pokud chceme přidat prvek do mapy, zapíšeme klíč do
hranatých závorek a pomocí operátoru rovná se vložíme prvek. Mapa,
podobně jako seznam, lze zapsat pomocí literálu, pro který se používají
složené závorky, ve kterých zapisujeme dvojice klíč: prvek
oddělené čárkou. V ukázce si namapujme postavy ze seriálu Simpsonovi na
jejich oblíbenou věc, abychom si ukázali, že mapa nemusí mít na rozdíl od
seznamu číselné klíče, ale že můžeme indexovat i řetězci:
Map<int, String> mapa = { 'Homer': 'kobliha', 'Marge': 'trouba', 'Bart': 'prak', 'Liza': 'kniha', 'Meggie': 'dudlík', };
keys
a values
Klíče a prvky mapy se dají získat z kolekcí keys
a
values
.
putIfAbsent()
Metoda vhodná pro přidání prvku pouze v případě, že již takový klíč neexistuje:
Map<int, String> mapa = { 0: 'nula', 1: 'uno', 2: 'два', }; mapa.putIfAbsent(2, () => 'dva'); // nepřepíše print(mapa); mapa[2] = 'zwei'; // přepíše print(mapa);
Výstup programu:
Konzolová aplikace
{0: nula, 1: uno, 2: два}
{0: nula, 1: uno, 2: zwei}
containsKey()
a
containsValue()
Metody sloužící pro určení, zdali se daný klíč nebo prvek nachází v kolekci:
Map<int, String> mapa = { 0: 'nula', 1: 'uno', 2: 'два', }; print(mapa.containsKey(2)); print(mapa.containsKey(3)); print(mapa.containsValue('uno')); print(mapa.containsValue('jedna'));
Výstup programu:
Konzolová aplikace
true
false
true
false
V příští lekci, Diář s databází v Dartu, si naprogramujeme pomocí kolekcí databázi,
bude to elektronický diář!
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 4x (2.29 kB)
Aplikace je včetně zdrojových kódů v jazyce Dart