27 - a Map kollekció¶
A Map
adatszerkezet szintén egy kollekció: kulcs-érték párokat tárolhatunk benne. Minden kulcshoz legfeljebb egy érték tartozhat.
Más programozási nyelvekben hívják dictionarynak vagy asszociatív tömbnek is.
inicializálás, lekérdezés¶
Ha úgy vesszük, a Vector
is egy Map, melyben a kulcsok Int
ek, mégpedig 0
-tól sorfolytonosan indexelt intekhez tartozik érték egy
korlátig. A Map
ebből annyiban tud többet, hogy generikus kulcsban is, értékben is: Map[K,V]
, két generikus paramétere
van, K
adja a kulcs, V
adja az érték típusát.
Az osztály használatára példa, ha mondjuk a use case az, hogy karakterekből számoljuk, melyikből mennyi van egy szövegben:
1 2 |
|
- Inicializálásnál
kulcs -> érték
párok sorozataként megadhatjuk a Mapet - Az
a
kulcshoz tartozó második beszúrás felülírta az elsőt: egy kulcshoz csak egy érték tartozhat - Nem kellett importolnunk: a scala.collection.immutable.Map a Predef Map.
Ha már elkészítettünk egy Map
et, akkor vannak lekérdező műveleteink, amik kulcs alapján adnak vissza értéket, de abban eltérnek,
hogy hogyan viselkednek, ha az adott kulcshoz nincs érték a Mapben:
get( key: K ): Option[V]
egy Optiont ad vissza: ha van(key,value)
pár ebben a Mapben, akkorSome(value)
az eredmény, ha a kulcs nem szerepel a Mapben, akkorNone
.apply( key: K ): V
ha szerepel akey
kulcs a Mapben, akkor viszaadja a hozzá tartozó értéket; ha nem, akkor dob egyNoSuchElementException
tgetOrElse( key: K, defaultValue: =>V ): V
hasonlóan azOption
ilyen nevű metódusához, ha szerepel akey
kulcs a Mapben, akkor visszaadja a hozzá tartozó értéket; ha nem, akkor kiértékeli a név szerint átadottdefaultValue
argumentumot és visszaadja aztcontains( key: K ): Boolean
igazat ad, ha akey
kulcs szerepel a Mapben.
Mik lesznek a fenti counts
Map esetén tehát az alábbi értékek?
1 2 3 4 5 6 7 8 |
|
Válasz mutatása
Meg tudjuk kapni a getOrElse
metódust másképp?
1 |
|
Válasz mutatása
funkcionális módosítás¶
Immutable kollekcióról lévén szó, itt is igaz, hogy nem mutátor metódusok vannak, hanem egy új Mapet kapunk vissza a következők kiértékelésekor:
updated( key: K, value: V ): Map[K,V]
- ha akey
kulccsal még nem szerepel érték, akkor beszúrjuk ezzel a kulccsal avalue
értéket; ha már szerepel benne, akkor a régi értéket kicseréljük avalue
-re.removed( key: K ): Map[K,V]
- a visszaadott Map ugyanaz, mint az eredeti, azzal, hogy ha volt benne akey
kulccsal érték, az újban már nem lesz.++( that: Map[K,V]): Map[K,V]
- athat
Mapben szereplő(key,value)
párokat beszúrja az eredeti Mapbe, szintén funkcionálisan.
Igaz-e, hogy map1 ++ map2 == map2 ++ map1
minden map1, map2
Mapre?
1 2 3 4 5 |
|
Válasz mutatása
Olyan értelemben számít ,,kollekciónak'', mintha egy (K,V)
pár típusú értékeket tároló kollekció lenne (mint pl. egy Set[(K,V)]
, azzal,
hogy nem csak hogy egyenlő párokból, de egyenlő kulcsú párokból sem lehet több: mintha ezeknek a pároknak az ==
metódusa konkrétan
a kulcsok közti egyenlőséget adná vissza.) Ezen az alapon már nem meglepő a flatMap
and co. szignatúrája:
flatMap[K2,V2]( f: ((K,V)) => Map[K2, V2]): Map[K2,V2]
. Ez a metódus az összes(key,value)
párra a Mapben kiszámoljaf((key,value))
-t, ami egy-egyMap[K2,V2]
lesz, majd ezeket a kapott Mapeket össze++
-olja. A sorrend nem specifikált alapértelmezésben, tehát ha van átfedés a készített Mapek kulcskészletében, akkor bármelyikükből jöhet a végérték!
Miért van dupla zárójel az f
-nél a K,V
pár körül?
Válasz mutatása
filter( p: ((K,V)) => Boolean ): Map[K,V]
szintén minden egyes(key,value)
párra vonatkozó predikátumot kap, nem csak a kulcsra vagy csak az értékre vonatkozótmap[K2,V2]( f: ((K,V)) => (K2,V2) ): Map[K2,V2]
minden egyes(key,value)
entryből készít egy(key2,value2)
entryt és ezekből az entrykből állít össze egy Mapet. Itt is igaz: ha van átfedés a készített entryk kulcsai közt, csak az egyik jelenik majd meg az eredményben!foreach[U]( f: ((K,V)) => U ): Unit
is az elvárt működést hozza, minden entryn kiértékeli azf
-et vélhetően mellékhatás végett.
a Map egyben K => V
függvény is¶
Ha tehát egy metódus egy f: K => V
függvényt vár paraméterben, akkor nyugodtan odaadhatunk neki egy Map[K,V]
-t, el fogja fogadni, lefordul:
1 2 3 4 |
|
Miért lehet ez?
Válasz mutatása
TODO gyakorlat: input stringben számoljunk karaktereket Mapbe! Huffman kódoljunk
TODO gyakorlat: vector[Int], Int: maradékosztályonként szedjük szét az input vektor elemeit. felkészül: groupBy
nem mind monád, ami flatMap¶
A Map
nem monád, mert (mainly az azonos kulcsok kezeléséből származó információvesztés végett) nem teljesíti az asszociativitási axiómát:
Hogy egy axióma nem teljesül, ahhoz elég egy ellenpéldát adnunk, vagyis egy m
Mapet, egy f
és egy g
flatmapni alkalmas függvényt, melyekre
nem jön ki egyenlőre a két oldal:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
m
, f
és g
függvényekkel a két eredmény egész más, pl. az alsóban szerepel a 3
-as kulcs, a felsőben meg nem.
Próbáljuk meg végigszámolni, hogy miért!
Válasz mutatása
ha a kulcsok rendezhetők, lehet SortedMap
is¶
Nagyon hasonló a Map underlying implementációja a Setéhez: elvégre, a kulcsokat valamiféle halmazban tárolja. Hasonlóan a Set
esetéhez,
itt is van SortedMap
ünk: ha beimportoljuk
1 |
|
Map
helyett SortedMap
pel példányosítunk,
ekkor a bejárás sorrendje garantáltan a kulcsok rendezése szerinti lesz.
A műveletek időigényéről itt is ugyanaz mondható el, mint a Set esetében: a rendezetlen Map átlagos viselkedése konstans időigény lesz
a lekérdezés és egy elem hozzáadása-elvétele műveletek esetén, ez kicsit megnőve logaritmikus lesz (nem 32-es alapú logaritmus, mint
a Vector
nál, hanem kettes alapú: tény, a kettő közt csak egy ötös szorzó van, tehát azért nem egy extrém nagy időigény ez),
ha rendezett Mapot használunk.