Toto je první článek seriálu o programování v Javě, který navazuje na původní seriál Petra Hatiny, přerušený loni v září. Budeme se zabývat funkcemi, které nám
usnadňují manipulace s poli a vykonávají za nás nepříjemnou "černou" práci.
25.2.2005 15:00 | Lukáš Jelínek | přečteno 57522×
Třída java.lang.System
obsahuje metodu arraycopy()
, která
umí zkopírovat prvky z jednoho pole do jiného. Lze kopírovat libovolný
počet prvků pole, nesmíme však samozřejmě "vyběhnout" mimo meze některého
z polí. Kopírovat lze i v rámci téhož pole, zdrojový a cílový úsek se
mohou překrývat (vnitřně to funguje tak, že se kopíruje do dočasného pole a pak
zpět do toho původního). Prvky pole mohou být jak primitivní, tak referenční
(objektové) typy (kopírují se samozřejmě jen odkazy, instance zůstavají
stejné).
Následující příklad ukazuje zkopírování části pole čísel typu double
do jiného takového pole (kopíruje se 5 prvků od indexu 3 ve zdrojovém
poli na cílové prvky od indexu 0):
double src[] = new double[10]; double dest[] = new double[5]; ... // zde se přiřazují prvky System.arraycopy(src, 3, dest, 0, 5);
Prvky primitivních typů se nesmí typově lišit. U referenčních typů je
povolena jen taková odlišnost, kdy lze cílovému prvku ten zdrojový přiřadit
(konkrétní typ se přitom může lišit). Porušení tohoto pravidla má za
následek vyhození výjimky ArrayStoreException
. V níže uvedeném
příkladu je ukázka kopírování prvků referenčního typu v rámci téhož pole
(ve čtyřprvkovém poli se první dva prvky zkopírují na zbývající místa):
Thread ta[] = new Thread[4]; ta[0] = new Thread(); ta[1] = new Thread(); System.arraycopy(ta, 0, ta, 2, 2);
Používání arraycopy()
namísto klasického cyklu s postupným přiřazováním
je (kromě usnadnění práce) výhodné také z hlediska rychlosti. Metoda
má totiž obvykle nativní implementaci, takže je rychlejší než kód
přímo v Javě.
S pouhým kopírováním polí se určitě nespokojíme, v praxi bývá potřeba
mnohem více operací. Právě pro tyto účely je určena třída
java.util.Arrays
,
poskytující množství statických metod pro práci s poli. Pro pole primitivních
typů je chování těchto metod předem dáno, u polí referenčních typů velice
záleží na vlastnostech a chování prvků. Tak se do toho pusťme...
Toto není metoda příliš typická pro pole (také byla přidána až ve verzi 1.5),
uvádím ji však jako první, protože se hodí pro ladění a testování programů.
Její smysl je podobný jako u stejnojmenné metody třídy Object
.
Funguje tak, že vytvoří řetězec, kde jsou v hranatých závorkách textové hodnoty
jednotlivých prvků. Zde jsou dva příklady:
int ia[] = new int[3]; ... // zde se přiřazují prvky System.out.println(Arrays.toString(ia)); Object oa[] = new Object[10]; ... // zde se přiřazují prvky System.out.println(Arrays.toString(oa));
Pokud je některý z prvků sám polem, nepřevedou se na text jeho prvky, nýbrž
se mu zavolá metoda toString()
z třídy Object
.
Často potřebujeme naplnit pole nebo jeho část nějakou konkrétní hodnotou.
K tomu slouží metoda fill()
, které na jediné zavolání pole takto
naplní. Máme dvě varianty této metody - jedna naplní celé pole, u druhé
určujeme počáteční a koncový index v poli (plní se počínaje počátečním
indexem až ke koncovému, ten už není naplněn). Nejlépe vše ukáže příklad
(první plní najednou celé pole, druhý postupně dvě části pole).
int ia[] = new int[100]; Arrays.fill(la, 0); String sa[] = new String[5]; Arrays.fill(sa, 0, 2, "text1"); Arrays.fill(sa, 2, 5, "text2");
Netřeba snad připomínat, že při plnění pole prvků referenčního typu se odkazuje ve všech prvcích na tentýž objekt.
Máme dvě pole a potřebujeme zjistit, zda jsou shodná. To dokáže metoda
equals()
, porovnávající dvě pole z hlediska počtu prvků a
jejich hodnot.
Dvě pole jsou shodná právě tehdy, když mají stejný počet prvků a
odpovídající prvky mají stejnou hodnotu; shodné jsou i dvě prázdné
(null
) reference na pole. Porovnávat lze pole prvků stejného
primitivního typu (pak se porovnávají hodnoty prvků), nebo pole
prvků referenčního typu (v tom případě jsou prvky shodné tehdy,
prohlásí-li je za shodné metoda equals()
dané třídy
nebo jsou obě reference prázdné).
byte ba1[] = new byte[10]; byte ba2[] = new byte[8]; boolean b = Arrays.equals(ba1, ba2); // vrátí false - různá délka
int ia1[] = new int[5]; int ia2[] = new int[5]; Arrays.fill(ia1, 0); Arrays.fill(ia2, 0); boolean b = Arrays.equals(ia1, ia2); // vrátí true - shodná pole ia2[3] = 5; b = Arrays.equals(ia1, ia2); // vrátí false - různé hodnoty 1 prvku
Řazení prvků pole patří mezi složitější úlohy, proto je dobře, že
je k dispozici mechanismus, který to za nás vykoná. Příslušná
statická metoda se jmenuje sort()
a opět může pracovat jak na celém
poli, tak na jeho části. Řadit můžeme pole primitivních prvků i
(s určitým omezením - viz dále) prvků referencí na objekty.
Prvky primitivních typů jsou řazeny optimalizovaným algoritmem
quicksort, řadícím v čase n*log(n).
long la[] = new long[1000]; ... // zde se přiřazují prvky Arrays.sort(la);
Prvky referenčních typů můžeme řadit pouze tehdy, implementuje-li
jejich třída rozhraní
java.lang.Comparable
(ještě o něm bude řeč někdy
později) a jsou navíc vzájemně porovnatelné (jejich metoda
compareTo()
musí akceptovat typ, s nímž se prvek srovnává). Na to je třeba dát
pozor a skutečně srovávat jen to, co srovnávat lze. Pro řazení
objektů se používá algoritmus mergesort, řazení je stabilní
(předem seřazené posloupnosti již nejsou měněny).
Object oa[] = new Object[3]; oa[0] = "abc"; oa[1] = "123"; oa[2] = "%%%%%"; Arrays.sort(oa); // toto lze oa[1] = new Double(1); Arrays.sort(oa); // nelze, způsobí výjimku ClassCastException
Poslední z věcí, na kterou se dnes podíváme, je vyhledávání prvku
v poli. To je zde implementováno jako hledání binárním dělením
- proto je nutné, aby pole bylo předem seřazeno metodou sort()
.
Pokud se neseřadí, není chování algoritmu definováno.
Metoda binarySearch()
vrací hledaného index prvku, nebo
zápornou hodnotu v případě nenalezení. Pro referenční typy platí stejná
pravidla jako u metody sort()
- protože tuto metodu musíme
stejně předtím zavolat, není již co řešit.
float fa[] = new float[200]; ... // zde se přiřazují prvky Arrays.sort(oa); // seřazení pole int i = Arrays.binarySearch(0.4); // vrátí index hledaného prvku
Pole jsou jednoduchou formou uchovávání většího množství dat. Na jejich omezení ale narazíme již v okamžiku, kdy potřebujeme měnit počet prvků nebo dělat nějaké složitější operace. Proto existují tzv. kontejnery (o nichž bude řeč příště), což jsou objekty, ve kterých máme uložena svá data a s jejichž pomocí můžeme provádět různé operace.