# Tabulka

- vztah mezi množinou klíčů a množinou hodnot (jako například slovník)
- asociativní abstraktní datová struktura

### Klíč

- diskrétní datový typ:
	- `int`
	- `String`
	- ne `double`!
- nejdůležitější vlastnost - možnost zjistit, zda jsou si dvě hodnoty rovny
- při vhodné implementaci vyhodnocení rovnosti může být klíčem i složitější struktura (např. množina čísel $\{0, 1, 2\}$ je shodná s $\{2, 0, 1\}$, může být tedy klíčem)

### Hodnota

- libovolný datový typ nebo reference na instanci

### Vlastnosti

- není určeno pořadí prvků
- některým klíčům nemusí být přiřazena žádná hodnota

### Operace

- přidání přiřazení (klíč + hodnota)
- získání hodnoty pro daný klíč
- zjištění existence hodnoty přiřazené danému klíči
- zrušení přířazení (odebrání klíče)
- získání všech klíčů s přiřazenou hodnotou

Ideálně by měly mít všechny (kromě všech klíčů) složitost $\Theta(1)$

### Implementace tabulky

**Tabulka s přímým adresováním**
- pole, kde index je klíč
- problém:
	- často neznáme rozsah možných klíčů
	- počet přípustných klíčů může být velmi velký nebo nekonečný
	- neřeší složitější klíče
- výhoda: splňuje $\Theta(1)$

**Reprezentace absence prvku**
- pomocí obalovací třídy (např. `Element`)
- ta obsahuje `value` s daty
- odlišíme tak neexistující klíč od `null`, `-1`, ...

**Rozptylová tabulka** (hash table)
- hashovací funkce
	- vstup: *klíč*, výstup: *index v poli*
	- vlastnosti:
		- hodnota 0 až délka pole -1
		- pro různé hodnoty různé klíče
			- nelze vždy, počet klíčů může být větší než délka pole
		- pro stejný klíč vrátí stejný index

**Kolize**
- vložíme hodnotu s klíčem $k_1$, uloží se na index $H(k_1)$
- vložíme další hodnotu s takovým klíčem $k_2$, že $H(k_1) = H(k_2)$
- pozice v poli už je obsazena
- nedá se jim dost dobře bránit, musí se řešit
	- do pole uložíme spojové struktury
	- každý záznam v poli obsahuje hodnotu i klíč
	- záznam bude obsahovat `next`, kam se dá případný další klíč s hodnotou

![](implementace-tabulky.png)

### Implementace operací

- přidání prvku
	- vložíme jej na začátek spojové struktury (nemusíme jí celou procházet)
- získání prvku
	- procházíme `while` cyklem
	- porovnáváme klíče - jeho instance musí být schopna rozhodnout o své významové shodnosti (`.equals()`)

### Výpočetní složitost

- přidání prvku: $\Theta(1)$
- vybrání a odebrání prvku závisí na délce spojové struktury
	- přidáno bylo $n$ prvků
	- používá se pole o délce $m$
	- složitost tedy je $\Theta(n/m) = \Theta(n)$
	- **řešení**:
		- omezit poměr $n/m$ (např. $n/m < 10$)
		- po překročení limitu se data přesunou do větší struktury
		- průměrná složitost je poté $\Theta(1)$

**Rozložení klíčů**
- známe-li rozložení klíčů, je potřeba mu přizpůsobit hashovací funkci
- pokud ne, funkce by měla záviset na všech částech klíče
	- ne jen na prvním písmenu, poslední číslici, ...

### Implementace v Javě

- třída `HashMap<K, V>`
- typové parametry `<K>` (klíč) a `<V>` (hodnota)

**Metody**
- `void put(K key, V value)`
- `V get(K key)`
- `boolean containsKey(K key)`
- v průměrném případě $\mathcal O(1)$

**Rozptylová funkce**
- používá se `public int hashCode() {...}` na datovém typu
- výsledek může být libovolné číslo (java se postará o namapování na indexy pole)

**Rovnost klíčů**
- používá se `boolean equals(Object o) {...}`

Při implementaci `equals` a `hashCode` musíme dbát na jejich konzistenci!
- když `a.equals(b) == true`, pak `a.hashCode() == b.hashCode()`
	- naopak to neplatí
- `a.equals(a)` musí vracet `true`
- když `a.equals(b) == true`, potom musí `b.equals(a) == true`

**Třída `HashSet<T>`**
- má pouze klíče - hodí se k efektivnímu nalezení duplicit