Dynamické pole (Vector, ArrayList) je datový kontejner (struktura určená k uchovávání dat) postavený nad polem. Dynamické pole umožňuje přidávat libovolný počet prvků, čímž eliminuje hlavní nevýhodu klasického pole – fixní velikost.
Princip
Dynamické pole ukládá své prvky do vnitřního pole fixní délky, v okamžiku, kdy je kapacita pole vyčerpána, dojde k alokaci nového většího pole (obvykle 2x většího z důvodu amortizace), všechny prvky původního pole se překopírují a staré pole se dealokuje. Obdobným způsobem se kontejner zachová, je-li příliš prázdný (poměr neobsazenosti překročí určitou mez) - zaalokoje se přiměřeně menší pole a opět se do něj hodnoty překopírují.
Složitost operací
Vkládání
Na první pohled je postup při vkládání prvku neefektivní, protože v okamžiku, kdy dojde místo, musí ArrayList veškerá data překopírovat. Asymptotická složitost vkládání prvku je proto .
Amortizovaná složitost vkládání
Pokud program běží dostatečně dlouho, tak se tato složitost vkládání amortizuje. To znamená, že se náklady na provedení této operace určitým způsobem v čase rozloží.
Představme si, že každému prvku dáme mincí (kde k je nějaká konstanta), kterými může platit za operace, které s ním nakládají. První minci prvek utratí při vložení do pole,
si jich může ponechat v banku na později.
V okamžiku, kdy je pole velikosti n plné, tak je v banku přesně mincí (pole bylo po minulé realokaci pouze z poloviny prázdné – natahujeme jej vždy dvojnásobně). Nyní kontejner vytvoří nové pole a kopíruje veškeré prvky. Za každé přenesení prvku z banku vezme k mincí (přenesení stojí k operací). Po překopírování všech prvků je bank prázdný a celou tuto proceduru ukládání a přenášení prvků můžeme opakovat.
Protože je konstanta (k je konstanta), tak je amortizovaná složitost vkládání
, jelikož jak amortizovaná, tak asymptotická složitost, ignorují multiplikativní konstanty.
Díky amorizované složitosti je struktura dynamického pole velmi rychlá, ač by tomu asymptotická složitost jejích operací nenapovídala. Na druhou stranu není její použití vhodné v real-time systémech, které musí garantovat určitou odezvu, protože vždy může dojít k onomu jednotlivému nepříznivému případu.
Mazání
Složitost operace mazání prvku závisí na konkrétní implementaci. Pokud mazaný prvek pouze prohodíme s posledním prvkem a místo na konci kontajneru uvolníme (prohlásíme za neobsazené), tak asymptotická složitost bude , ale dojde ke zpřeházení prvků. Pokud odstranění provedeme tak, že všechny další prvky posuneme o jedno místo, bude asymptotická složitost
, ale pořadí prvků zůstane zachováno.
Čtení
Čtení prvku na indexu i proběhne v , protože pole umožňuje náhodný přístup.
Kód:
001.
public
class
ArrayList {
002.
private
final
int
defaultSize;
003.
private
int
size;
004.
private
int
[] array;
005.
006.
/**
007.
* Konstruktor - vychozi kapacita listu je 4
008.
*/
009.
public
ArrayList(){
010.
this
(
4
);
011.
}
012.
013.
/**
014.
* Konstruktor, lze nastavit vychozi delku listu, pod tuto nebude nikdy zkracen
015.
* @param size
016.
*/
017.
public
ArrayList(
int
size){
018.
this
.size =
0
;
019.
this
.defaultSize = size;
020.
array =
new
int
[size];
021.
}
022.
023.
/**
024.
* Prida prvek do arraylistu
025.
* @param i prvek k pridani
026.
*/
027.
public
void
add(
int
i){
028.
if
(getSize() == array.length){
029.
this
.enlarge();
030.
}
031.
array[size] = i;
032.
size++;
033.
}
034.
035.
/**
036.
* Odstrani prvek na zadanem indexu
037.
* @param index index prvku k odstraneni
038.
*/
039.
public
void
remove(
int
index){
040.
array[index] = array[getSize() -
1
];
041.
size--;
042.
trim();
043.
}
044.
045.
/**
046.
* Vrati prvek na zadanem indexu
047.
* @param index index prvku
048.
* @return pozadovany prvek
049.
*/
050.
public
int
get(
int
index){
051.
if
(index >= getSize())
throw
new
IndexOutOfBoundsException(
"mimo meze"
);
052.
return
array[index];
053.
}
054.
055.
/**
056.
* Prodlouzi pole o polovinu, pokud je plne
057.
*/
058.
private
void
enlarge(){
059.
int
[] newArray =
new
int
[getSize() *
2
];
060.
for
(
int
i =
0
; i < getSize(); i++) {
061.
newArray[i] = array[i];
062.
}
063.
array = newArray;
064.
}
065.
066.
/**
067.
* Pokud je pole plne mene nez z ctvrtiny, tak ho zkratime na polovinu
068.
* (pokud by to nebylo mene, nez je vychozi delka)
069.
*/
070.
private
void
trim(){
071.
if
((array.length/
4
) > getSize() && array.length/
2
>= getDefaultSize()){
072.
int
[] newArray =
new
int
[array.length/
2
];
073.
for
(
int
i =
0
; i < getSize(); i++) {
074.
newArray[i] = array[i];
075.
}
076.
array = newArray;
077.
}
078.
}
079.
080.
@Override
081.
public
String toString() {
082.
StringBuilder builder =
new
StringBuilder();
083.
for
(
int
i =
0
; i < size; i++) {
084.
builder.append(array[i]);
085.
builder.append(
" "
);
086.
}
087.
builder.append(
"\\narray.length: "
);
088.
builder.append(array.length);
089.
builder.append(
", size: "
);
090.
builder.append(size);
091.
builder.append(
", default size: "
);
092.
builder.append(defaultSize);
093.
return
builder.toString();
094.
}
095.
096.
/**
097.
* Getter na vychozi delku pole
098.
* @return vychozi delka pole
099.
*/
100.
public
int
getDefaultSize() {
101.
return
defaultSize;
102.
}
103.
104.
/**
105.
* Vrati pocet prvku ulozenych v poli
106.
* @return pocet ulozenych prvku
107.
*/
108.
public
int
getSize() {
109.
return
size;
110.
}
111.
}