IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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:

Pole v Kotlinu - Kolekcia v Kotlin

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:

Kolekcia ArrayList v Kotlin - Kolekcia v Kotlin

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áti true, 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 v ArrayListu 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ódy removeRange() alebo -=. Je dobrý nápad metódu využívať, keďže nám ušetrí cyklus.
  • toTypedArray() - Vráti inštanciu Array, 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ódy indexOf(), 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 rozhrania Comparable, inak metóda vyvolá výnimku. Základné triedy a štruktúry z Kotlinu Comparable 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

 

Predchádzajúci článok
Úvod do kolekcií a genericita v Kotlin
Všetky články v sekcii
Kolekcia v Kotlin
Preskočiť článok
(neodporúčame)
Spojový zoznam v Kotlin
Článok pre vás napísal Samuel Kodytek
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje všem jazykům okolo JVM. Rád pomáhá lidem, kteří se zajímají o programování. Věří, že všichni mají šanci se naučit programovat, jen je potřeba prorazit tu bariéru, který se říká lenost.
Aktivity