2. diel - Zoznam (List) pomocou poľa v Kotlin
V minulej lekcii, Úvod do kolekcií a genericita v Kotlin , sme si urobili úvod do kolekcií a ukázali sme si, čo je to genericita. Dnes sa budeme v Kotlin tutoriálu venovať zoznamom (listom).
Poľa
Urobme si na začiatku malú odbočku späť k poľu, ktoré bolo prvou kolekciou, ktorú sme v kurze spoznali. Pole sa vyznačuje tým, že má pevne daný počet prvkov, ktorý nemôžeme nikdy zmeniť. Pokiaľ do už existujúceho poľa chceme niečo pridať, musíme vytvoriť nové. Z tohto dôvodu poľa niekedy dokonca nebýva považované ani za kolekciu. Ukážme si príklad:
val pole = arrayOf(1, 2, 3) val pole2 = pole + 4 // pole2 je již nová instance a nemá vůbec nic společného s proměnnou pole println(pole.size) // velikost původního pole je stále 3, protože nemůžeme změnit velikost aktuální instance
Ako si môžeme všimnúť, Kotlin obmedzenia spôsobené nemennú dĺžkou pole šikovne obchádza cez imutabilní triedu, ktorá obaľuje poľa z JVM. Prvky v poli sú číselne indexované a to od nuly.
Keďže dáta sú rovnakého typu (či už úplne rovnakého, alebo spoločného predka), zaberajú v pamäti rovnako miesta. Jednotlivé prvky poľa sú v pamäti uložené za sebou, ako v rade, ktorá je neprerušená. Pole celých čísel si môžeme predstaviť napr. Nasledovne:
Ak teda chceme napr. Pristúpiť na 5. prvok, len vstúpime tam, kde pole
začína a potom odskočíme 4 násobky veľkosti typu (tu Int
u)
ďalej. Sme na 5. prvku. Čítanie a zápis na indexy v poli má teda
konštantný časovú zložitosť. Ak vás tento termín zmiatol,
môžete to chápať tak, že do poľa zapisujeme okamžite a rovnako tak z neho
aj čítame.
Hlavnou nevýhodou poľa teda je, že do neho nemôžeme za behu aplikácie prvky pridávať alebo ich mazať. To, žiaľ, často potrebujeme. Predstavme si vyrábať stále nové inštancie pole (teda jeho kópia) o viac ako 1000 prvkoch, program by sa nám mohol radikálne spomaliť.
Zoznamy (listy)
Zoznamy (anglicky a často i slovensky listy) sú kolekcie, ktoré umožňujú prvky za behu programu pridávať a mazať. Môžu byť číselne indexované ako polia, ale tiež nemusí. Sú v zásade 2 typy zoznamov: Zoznamy s poľom a spojové zoznamy. Spojové zoznamy Kotlin nepodporuje a v prípade potreby by sme sa museli obrátiť na štandardné javovské kolekcie.
Zoznamy s poľom
Zoznam najčastejšie využíva toho, že hoci veľkosť poľa nemôžeme za behu programu meniť, môžeme za behu vytvoriť pole nové.
Zoznam je potom trieda, ktorá obsahuje na rozdiel od poľa metódy pre
pridanie a navyše odstránenie prvkov (a mnoho ďalších, pre nás teraz
nepodstatných metód). Trieda v podstate obaľuje pole a obsahuje navyše
premennú, kde si uchováva počet prvkov. Pri vytvorení inštancie sa vytvorí
pole napr. O 12tich prvkoch a premenná s počtom prvkov sa nastaví na
0
. Pri pridaní prvého prvku sa prvok vloží na 1. index v poli a
počet prvkov sa inkrementuje. Takto môžeme pridať až 12 prvkov, než polia
naplníme. Vo chvíli, keď vyčerpáme kapacitu poľa, jednoducho vytvoríme
nové, treba 2x väčší. Prvky zo starého poľa do neho skopírujeme a staré
polia zahodíme. Až sa toto nové pole opäť naplní, budeme situáciu
opakovať. Takýmto spôsobom naozaj interne funguje Kotlin kolekcie
ArrayList
, s ktorou sme sa doteraz nestretli.
ArrayList
s poľom si môžeme predstaviť asi takto:
ArrayList
na obrázku má 8 prvkov. Prvky sú uložené v
internom poli, ktoré má prvkov 12. Posledné 4 prvky sa nevyužívajú a
ArrayList
sa zvonku tvári ako že tam nie sú.
Výhodou je rýchlosť prístupu k prvkom pomocou indexov vďaka využitiu poľa. Nevýhodou je samozrejme časové oneskorenie potrebná na vytvorenie nového poľa a prekopírovanie prvkov, aj keď nastáva len občas na rozdiel od klasického poľa v Kotlinu. Ďalšie, aj keď menej bolestivú nevýhodou, je, že kolekcia zaberá v pamäti viac priestoru, než je nutné. Tento typ zoznamu je napriek tomu najpoužívanejší kolekcií v Jave a je pomerne dobre optimalizovaný.
List s poľom je teda v Kotlinu zastúpený triedou ArrayList
.
Ukážme si ako založiť ArrayList
a následne si popíšme
dôležité metódy na triede ArrayList
:
Inicializáciu kolekcie môžeme vykonať nasledovne:
val array = arrayListOf(1, 2, 3)
Alebo môžeme tiež použiť:
val array = mutableListOf(1, 2, 3)
Kolekcia ArrayList
v Kotlinu je totožná s tou v
Jave. Kotlín ju využíva.
Metódy a ďalšie prvky na triede ArrayList
Javovský ArrayList
implementuje interface List
.
Kotlín síce používa javovský ArrayList
, ale pridáva si aj
vlastné metódy, ktoré sa wrapují okolo toho javovského
ArrayList
u, aby viac pripomínal kolekciu v Kotlinu. Základné
metódy teda sú:
clear()
- Odstráni všetky prvky.contains()
- Vrátitrue
, ak list obsahuje daný prvok.size
- Vráti počet prvkov v liste.get()
alebo[]
- Vráti prvok na danej pozícii.add()
alebo+=
- Pridá nový prvok na koniec liste alebo na daný index vloží nový prvok (a ďalšie prvky posunie).remove()
alebo-=
- Odstráni daný prvok. Táto funkcia je veľmi užitočná v prípade, že máme vArrayListu
inštancie nejakej triedy (napr. Používateľa), nemusíme si držať ich číselné indexy, len zavoláme napr.list.remove(karel)
.removeAt()
- Odstráni prvok na danom indexe.
Hoci to môže byť mätúce, tak -=
a
+=
nevracia novú inštanciu
ArrayList
u.
List
Môžeme tiež použiť listOf()
, ktorý je
imutabilní a v Kotlinu sa často používa. Po každom pridaní alebo odobratie
vráti novú inštanciu. Pretože by metódy ako add()
, ktoré
vracajú Unit
(nič), nedávali príliš zmysel na kolekciu, ktorá
je nemenná, už je tu nenájdeme. Preťažené operátory však dostupné máme
(+=
, -=
) a vracajú vždy novú inštanciu.
Kedy použiť List a kedy ArrayList?
ArrayList
a List
sú teda obidva zoznamy a môžeme
použiť obaja k tomu samému účelu. Čím sa teda líši? Ako som už
spomínal, premenná typu List
je immutabilní (nemenná) a každá
operácia s ňou vytvorí vždy novú inštanciu zoznamu. Na druhú stranu
ArrayList
meniť možno. Predstavme si, že máme nejakú 2d hru,
ktorá má hracie pole reprezentované 2d poľom (napr. Nejaké šach alebo
piškvorky). Pre tento prípad je výhodnejšie použiť ArrayList
,
pretože je zbytočné vytvárať stále nové inštancie našej kolekcie, keď
napr. Zmeníme iba jeden objekt v nej. Keby sme posunuli pešiaka o políčko
dopredu a kvôli tomu by sme vytvorili úplne nové "pole", nie je to
zbytočné?
ArrayList
má ale aj svoje nevýhody, napríklad z neho
nemôžeme nijako jednoducho mazať, keď ním iterujeme. To je preto, že
natierame rovno na tej inštanciu, ktorú iterujeme.
Väčšinou sa používa List
, ak ho potrebujete však často
meniť, je dobré zvážiť použitie ArrayList
u. Kvôli
chýbajúcej immutabilitě môžete však naraziť na problémy, ak pracujete s
vláknami.
Príklad
Hoci sme si prácu s metódami na Array
aj vyskúšali už
1000-krát a práca s ArrayList
em nie je v podstate nijako
odlišná, pre úplnosť si predsa len ukážme niekoľko riadkov kódu:
val array = arrayListOf<Int>(1) array += 2 array -= 1 // Můžeme mazat! println(array)
Výstup programu:
[2]
Kód vyššie vytvorí ArrayList
typu Int
s prvkom
1
, pridá do neho prvok 2
a následne prvok
1
odstráni. Výsledný zoznam sa vypíše do konzoly. S indexy
pracujeme ako by sme pracovali s poľom, ale môžeme do neho za behu programu
pridávať prvky a tiež je mazať na rovnakej inštanciu.
Samotný ArrayList
ešte dodáva ďalšie metódy, popíšme si
aj tie:
addAll()
alebo+=
- Pridá do listu prvky z odovzdaného poľa. Podobne môžeme volať aj metódyremoveRange()
alebo-=
. Je dobrý nápad metódu využívať, keďže nám ušetrí cyklus.toTypedArray()
- Vráti inštanciuArray
, z ktorého možno prvky iba čítať. Vhodné pre zapuzdrenie prvkov kolekcie.size
- Vlastnosť nesúci počet prvkov v liste.lastIndexOf
() - Obdoba metódyindexOf()
, vracia index posledného výskytu daného prvku v liste.removeAll()
- Odstráni všetky prvky, ktoré sa odoslali v argumentu v poli.reverse()
- prevráti list tak, aby bol 1. prvok ako posledný a naopak posledný ako prvý.sort()
- Setřídí list. Je dôležité, aby jeho prvky obsahovali rozhraniaComparable
, inak metóda vyvolá výnimku. Základné triedy a štruktúry z KotlinuComparable
implementujú, u svojich tried ho vieme dodať.
Pridávať rozsah môžeme aj operátorom +=
:
array += (1..5)
Toto nám zatiaľ bude stačiť. Až si vysvetlíme lambda
funkcie, tak si ukážeme ďalšie metódy ako filter()
alebo
map()
.
Vyskúšajte si ďalšie metódy ako sort()
, vyhľadávanie a
podobne. Detailnejšie prácu s kolekciami sa budeme ešte venovať.
V budúcej lekcii, Spojový zoznam v Kotlin , si urobíme odbočku k viacrozmerným poliam, ak ste ich náhodou pri výučbe preskočili, a potom sa pozrieme na slovníky a množiny v Kotlin.
V budúcej lekcii, Spojový zoznam v Kotlin , sa pozrieme na spojové zoznamy, popíšeme, ako fungujú a aké metódy ich implementácia ponúka.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 13x (7.31 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin