Java 10
Ukladanie objektov. Polia, kontajnery
Cieľom kapitoly je oboznámiť sa s ďalšími možnosťami práce s poľami a s
niektorými triedami kontajnerov.
Kontajner je objekt, ktorý je určený na skladovanie iných objektov a na ich spätné vyberanie. Zatiaľ sme sa oboznámili s jedným typom kontajnera – s poľom. Niekedy by sme potrebovali ukladať objekty inak, než ako to umožňuje pole. Napríklad by sme mali radi možnosť, aby kontajner nebol pri svojom vytvorení obmedzený presným počtom prvkov, ktoré dokáže prijať. Aby sa jeho kapacita dala podľa potreby pohodlne zväčšovať. Niekedy je pri uskladnení výhodnejšiee ukladať prvky nie podľa číselného indexu, ale podľa nejakého iného kľúča, Poprípade by sme radi nejaký súbor prvkov vnímali a ukladali ako množinu. Všetky tieto možnosti Java ponúka. Práci s kontajnermi by sa dala venovať celá kniha, my si tu predstavíme len základné typy kontajnerov.
Ešte raz polia
Keď raz
definujeme typ poľa, (napr. Integer[]
p), pole objekty iných tried neuskladní, už prekladač nás upozorní na nekompatibilitu
typov. Príklad, ako môžeme a ako nemôžeme rozširovať-zužovať typy si pozrime na
nasledujúcom príklade. Najprv vytvorme balík, určený na obsluhovanie výstupov a
v ňom triedu Tlač, s metódou
„pole(Object[] o)“, ktorá bude tlačiť zadané pole. Potom vytvoríme triedu TypPola ,
naplníme v nej a vytlačíme rôzne polia.
Kód P10.1
package vystup;
public class Tlac {
public static void pole(Object[]
pole){
for (int i = 0; i < pole.length; i++){
System.out.println(pole[i]);
}//EOFor
}//EOMain
}//EOClass
Kód P10.2
import vystup.*;
class Cosi {
int i = 5;
public Cosi(int i){
this.i = i;
}
public void zmenSa(){
i = i +1;
}
/*public String toString(){
return "Cosi s hodnotou " + String.valueOf(i);
}*/
}
public class TypPola {
public static void
main(String[] args){
String[] poleS = new
String[3];
Integer[] poleI =
new Integer[3];
Cosi[] poleC = new
Cosi[3];
Object os = new
String("tento objekt je String ");
//poleS[0] = os;
//prekladac vyhlasi chybu
poleS[0] =
(String)os;
for(int j = 1; j
< poleI.length; j++){
int k =
(int)(Math.random()*2004);
poleI[j]
=new Integer(k);
poleS[j] =
"cislo " + poleI[j].toString();
poleC[j] =
new Cosi(2*k);
}
Tlac.pole(poleI);
Tlac.pole(poleS);
Tlac.pole(poleC);
}
}
Výpis z programu:
null
6
3
tento objekt je String
cislo 6
cislo 3
null
Cosi@3179c3
Cosi@310d42
Komentáre
Vidíme, že nezaplnené prvky polí mali hodnotu „null“. Takisto vidíme, že
tlačenie prvkov triedy „Cosi“ možno neprebehlo podľa našich predstáv. Statická
metóda Tlac.pole() zoberie prvok poľa a pretypuje ho na prvok triedy
Object. Potom použije službu metódy „toString()“, ktorá, v rámci
polymorfizmu, vytlačí textovú reprezentáciu prvku tej-ktorej triedy. Keďže
trieda Cosi neprekryla metódu „toString()“ svojej materskej triedy Object,
použila sa metóda materskej triedy. Ak by sme v kóde 10.2 bola
odkomentovaná definícia metódy toString(), dostali by sme napríklad takýto
výpis:
null
3
2
tento objekt je String
cislo 3
cislo 2
null
Cosi s hodnotou 6
Cosi s hodnotou 4
Ďalšie metódy pri práci s poľami
Pozrite si nasledujúce metódy
v dokumentácii:
Metódu System.arraycopy(), ktorá
umožňuje kopírovať prvky z poľa do poľa.
Metódu Arrays.equals(parametre),
ktorá umožňuje zistiť, či sa dve polia rovnajú.
Metódu Arrays.sort(parametre)
umožňujúcu usporiadať pole alebo časť poľa ktoré obsahuje buď prvky
primitívnych dátových typov, alebo objekty triedy, ktorá implementuje rozhranie
Comparable alebo je s ňou združený komparátor.
Metódu Arrays.binarySearch(parametre),
určenú na binárne prehľadávanie usporiadaného poľa.
Ukážka práce s týmito metódami:
Kód P10.3
// kvoli rozhraniu Comparator:
import java.util.*;
import vystup.*;
class Basketbalista {
String meno =
"neznamy";
int vyska = 180 +
(int)(Math.random()*101);
public Basketbalista(String
meno){
this.meno = meno;
}
public int getVyska(){
return vyska;
}
public String toString(){
return meno +" s vyskou " + String.valueOf(vyska) +
" cm";
}
}
class BasketComparator implements Comparator{
public int compare(Object
o1,Object o2){
Basketbalista b1 = (Basketbalista)o1;
Basketbalista b2 = (Basketbalista)o2;
return b1.getVyska() - b2.getVyska();
}
}
public class MetodyArrays{
public static void main(String[]
args){
int dlzka = 3;
Basketbalista[] grupa1 = new Basketbalista[dlzka];
Basketbalista[] grupa2 = new Basketbalista[dlzka];
Basketbalista[] grupa3 = new Basketbalista[dlzka];
for(int j = 0; j < dlzka;
j++){
double nahodny =
Math.random() *100000;
String meno
="basketbalista " +
(String.valueOf(nahodny)).substring(0,5);
grupa1[j] = grupa2[j] =new
Basketbalista(meno);
grupa3[j] = new
Basketbalista("grupa3 hrac c. " + String.valueOf(j));
}
System.out.println("grupa1 == grupa2:" +
Arrays.equals(grupa1,grupa2));
// usporiadam grupu 1
BasketComparator comp = new BasketComparator();
Arrays.sort(grupa1,comp);
System.out.println("grupa1 == grupa2:" +
Arrays.equals(grupa1,grupa2));
Tlac.pole(grupa1);
Tlac.pole(grupa3);
Basketbalista[] tim = new
Basketbalista[10];
Basketbalista klucovy = new
Basketbalista("Tiger Jordan");
tim[0] = klucovy;
//kopirujem do timu hracov
System.arraycopy(grupa1,0,tim,1,3);
System.arraycopy(grupa2,0,tim,4,3);
System.arraycopy(grupa3,0,tim,7,3);
Tlac.pole(tim);
Arrays.sort(tim,comp);
// ideme hladat klucoveho
basketbalistu
int kdeJe =
Arrays.binarySearch(tim, klucovy,comp);
System.out.println("klucovy
bol: " + klucovy);
System.out.println("Nasiel som na indexe " + kdeJe +"
hraca " + tim[kdeJe]);
}//EOMain
}
Jeden z výpisov:
grupa1 == grupa2:true
grupa1 == grupa2:false
basketbalista 25247 s vyskou 182 cm
basketbalista 97136 s vyskou 212 cm
basketbalista 90680 s vyskou 271 cm
grupa3 hrac c. 0 s vyskou 264 cm
grupa3 hrac c. 1 s vyskou 275 cm
grupa3 hrac c. 2 s vyskou 199 cm
Tiger Jordan s vyskou 212 cm
basketbalista 25247 s vyskou 182 cm
basketbalista 97136 s vyskou 212 cm
basketbalista 90680 s vyskou 271 cm
basketbalista 25247 s vyskou 182 cm
basketbalista 90680 s vyskou 271 cm
basketbalista 97136 s vyskou 212 cm
grupa3 hrac c. 0 s vyskou 264 cm
grupa3 hrac c. 1 s vyskou 275 cm
grupa3 hrac c. 2 s vyskou 199 cm
klucovy bol: Tiger Jordan s vyskou 212 cm
Nasiel som na indexe 4 hraca basketbalista 97136 s vyskou 212 cm
Poznámka : BasketComparator
stotožňuje dvoch hráčov, ak majú rovnakú výšku.
ArrayList, trieda
implementujúca rozhranie Collection
Knižnica java.util
obsahuje rozhranie Collection, slúžiace na implementáciu viacerých kontajnerov.
Pozrite si toto rozhranie v dokumentácii. Tu spomeňme aspoň niektoré
z jeho metód:
boolean add(Object o)
boolean remove(Object o)
int size()
Object toArray() - vytvorí
z daného kontajneru pole objektov
Jednou z tried
implementujúcich rozhranie Collection je ArrayList. Umožňuje uchovávať objekty
ľubovoľných tried. Na rozdiel od poľa pri jeho vytvorení nedeklarujeme pevnú
dĺžku, je schopný jednoduchým spôsobom zväčšovať svoju skladovaciu kapacitu. Vymenujme si ešte
ďalšie metódy, užitočné pri používaní ArrayListu:
boolean add(Object o)
Object get(int index)
Na to, aby sme vedeli, či ArrayList nejaký objekt obsahuje a na akom
indexe je objekt uložený, slúžia metódy
boolean contains(Object element)
int indexOf(Object element).
Iterátor
V rozhraní Collection nájdeme aj metódu
Iterator iterator()
Iterátor je jednoduchý objekt,
ktorý toho dokáže veľmi málo. Práve preto ale málo zaťažuje pamäť. Bežne
iterátor používame, ak chceme prvok po prvku spracovať jeho obsah. Na to nám
slúžia metódy boolean hasNext() (zistíme, či iterátor ešte obsahuje nejaké
prvky) a Object next() (iterátor nám poskytne ďalší prvok v poradí).
Prácu s ArrayListom a Iterátorom si ukážeme na nasledujúcom
jednoduchom príklade. Najprv v ňom náhodne zaplníme ArrayList rôznymi
objektami. Potom zistíme, ktoré z týchto objektov sú inštancie triedy
Princezna. Tie umiestnime do osobitného ArrayListu. Tento vytlačíme. Potom z
neho vytvoríme pole. Prvky poľa
vytlačíme. V príklade uvidíme takisto prácu s iterátorom.
Kód P10.4
import java.util.*;
class Princezna {
String meno =
"Defaultka";
int vek = 18;
// polia sluziace na inicializaciu
String[] spoluhlasky = new
String[]{"b","c","d","f","g","h","j","k","l","m","n","p","r","s","t","v","x","z"};
String[] samohlasky = new
String[]{"a","e","i","o","u"};
{
String m = dajNahodne(spoluhlasky).toUpperCase();
m+= dajNahodne(samohlasky);
m+=dajNahodne(spoluhlasky);
m+=dajNahodne(samohlasky);
m+=dajNahodne(spoluhlasky);
m+="a";
this.meno = m;
this.vek = 14 +((int)(Math.random()*11));
}
private String dajNahodne(String[]
pole){
int index =
(int)(Math.random()*pole.length);
return pole[index];
}
public String toString(){
return "Princezna "
+meno + ", "+ String.valueOf(vek) + " rokov.";
}
}
public class PracaSArrayListom {
public static void
main(String[] args){
ArrayList nahodny = new
ArrayList();
Princezna prvaPrincezna = new
Princezna();
nahodny.add(prvaPrincezna);
for(int j = 0; j < 50; j++)
{
int k =(int)(Math.random()*4);
switch(k){
case 0: nahodny.add(new Integer(j));
break;
case 1: nahodny.add("String " + String.valueOf(j));
break;
case 2: nahodny.add(new Princezna());
break;
default: nahodny.add(new Boolean(j==10));
}//EOSwitch
}//EOFor
// zistime, ci obsahuje prvu
Princeznu a na akom indexe
System.out.println("Prva
je tam =" + nahodny.contains(prvaPrincezna));
System.out.println("Je na
indexe " + nahodny.indexOf(prvaPrincezna));
System.out.println("Premiesame
poradie.");
Collections.shuffle(nahodny);
System.out.println("Teraz
je na indexe " + nahodny.indexOf(prvaPrincezna));
// exportujeme Princezny
ArrayList princezny = new ArrayList();
Iterator vsetkyPrvky =
nahodny.iterator();
while(vsetkyPrvky.hasNext()){
Object vybrany = vsetkyPrvky.next();
//je to Princezna?
if(vybrany instanceof Princezna){
princezny.add(vybrany);
}
}//EOWhile
System.out.println(princezny);
Object[] princesses = princezny.toArray();
// tu nam s Princeznami staci pracovat ako s instanciami triedy
Object
// vdaka polymorfizmu sa vyvola nami definovana metoda toString
// ine verejne metody Princezna aj tak nema
System.out.println("Princezien je spolu " +
princesses.length + ":");
for(int p = 0; p <
princesses.length; p++){
if(p < 10) System.out.print("
");
System.out.println( p + ". " +
princesses[p]);
}
}//EOMain
}//EOClass
Jeden z výpisov:
Prva je tam =true
Je na indexe 0
Premiesame poradie.
Teraz je na indexe 7
[Princezna Zijava, 21 rokov., Princezna Fixala, 17 rokov., Princezna
Porija, 18 rokov., Princezna Makiva, 17 rokov., Princezna Hiteta, 15 rokov.,
Princezna Xinuna, 21 rokov., Princezna Junika, 20 rokov., Princezna Coleca, 18
rokov., Princezna Mehula, 24 rokov., Princezna Vamada, 18 rokov., Princezna
Kadoma, 24 rokov., Princezna Mopoka, 15 rokov.]
Princezien je spolu 12:
0. Princezna Zijava, 21 rokov.
1. Princezna Fixala, 17 rokov.
2. Princezna Porija, 18 rokov.
3. Princezna Makiva, 17 rokov.
4. Princezna Hiteta, 15 rokov.
5. Princezna Xinuna, 21 rokov.
6. Princezna Junika, 20 rokov.
7. Princezna Coleca, 18 rokov.
8. Princezna Mehula, 24 rokov.
9. Princezna Vamada, 18 rokov.
10. Princezna Kadoma, 24 rokov.
11. Princezna Mopoka, 15 rokov.
Mapy
Niekedy by sa nám hodilo ukladať objekty do kontajnera tak, že ich
„indexami“ by neboli čísla, ale inštancie nejakej triedy. Napríklad indexy by
boli krajiny, uložené objekty ich hlavné mestá. Predstavme si tu stručne triedu
HashMap, ktorá je schopná vykonávať takúto činnosť.
Táto trieda implementuje rozhranie „Map“. Tu sú niektoré jeho metódy:
Object put (Object kľúč,Object hodnota) – pridá do mapy hodnotu
a takisto aj kľúč, pomocou ktorého môžeme hodnotu nájsť
Object get(Object kľúč) - vráti
hodnotu, ktorá zodpovedá danému kľúču
boolean containsKey(Object kľúč) – zistí, či mapa obsahuje daný kľúč
boolean containsValue(Object hodnota) zistí, či mapa obsahuje danú hodnotu.
Kontajnery typu HashMap sú rýchle,
pretože používajú pri prehľadávaní hešovací kód. Príbuzná trieda – TreeMap, sa
používa, ak chceme mať výsledky zobrazené už v usporiadanom tvare (podľa
rozhrania Comparable alebo Comparator). Nasledujúca ukážka nám znova pripomína,
že kontajnery obsahujú odkazy, nie objekty.
Kód P10.6
import java.util.*;
public class ZmenaOdkazu {
public static void
main(String[] args){
HashMap mapa = new
HashMap();
String prvyKluc =
"prvy";
String druhyKluc =
"druhy";
mapa.put(prvyKluc,
new Integer(1));
mapa.put(druhyKluc,new
Integer(2));
System.out.println(mapa.get("prvy"));
System.out.println(mapa.get(prvyKluc));
prvyKluc = druhyKluc;
System.out.println(mapa.get(prvyKluc));
}
}
Výpis:
1
1
2
Na záver uveďme ešte raz Kód P1.1, s ktorým sme kedysi
rozprávanie o Jave začínali. Prezrite si ho a sledujte v ňom
prácu kontajnerov. (Trieda TreeMap, rozhrania Collection, Set.) Pozrite si
v dokumentácii metódy, používané
v súvislosti s kontajnermi.
Kód P10.6 = Kód P1.1
//:! :CopyRight.txt
Copyright ©2000 Bruce Eckel
Source code file from the 2nd edition of the book
"Thinking in Java." All rights reserved EXCEPT as
allowed by the following statements:
You can freely use this file
for your own work (personal or commercial),
including modifications and distribution in
executable form only. Permission is granted to use
this file in classroom situations, including its
use in presentation materials, as long as the book
"Thinking in Java" is cited as the source.
Except in classroom situations, you cannot copy
and distribute this code; instead, the sole
distribution point is http://www.BruceEckel.com
(and official mirror sites) where it is
freely available. You cannot remove this
copyright and notice. You cannot distribute
modified versions of the source code in this
package. You cannot use this file in printed
media without the express permission of the
author. Bruce Eckel makes no representation about
the suitability of this software for any purpose.
It is provided "as is" without express or implied
warranty of any kind, including any implied
warranty of merchantability, fitness for a
particular purpose or non-infringement. The entire
risk as to the quality and performance of the
software is with you. Bruce Eckel and the
publisher shall not be liable for any damages
suffered by you or any third party as a result of
using or distributing software. In no event will
Bruce Eckel or the publisher be liable for any
lost revenue, profit, or data, or for direct,
indirect, special, consequential, incidental, or
punitive damages, however caused and regardless of
the theory of liability, arising out of the use of
or inability to use software, even if Bruce Eckel
and the publisher have been advised of the
possibility of such damages. Should the software
prove defective, you assume the cost of all
necessary servicing, repair, or correction. If you
think you've found an error, please submit the
correction using the form you will find at
www.BruceEckel.com. (Please use the same
form for non-code errors found in the book.)
///:~
//: c11:WordCount.java
// Counts words from a file, outputs
// results in sorted form.
import java.io.*;
import java.util.*;
class Counter {
private int i = 1;
int read() { return i; }
void increment() { i++; }
}
public class WordCount {
private FileReader file;
private StreamTokenizer st;
// A TreeMap keeps keys in sorted
order:
private TreeMap counts = new
TreeMap();
WordCount(String filename)
throws FileNotFoundException {
try {
file = new
FileReader(filename);
st = new StreamTokenizer(
new BufferedReader(file));
st.ordinaryChar('.');
st.ordinaryChar('-');
} catch(FileNotFoundException e)
{
System.err.println(
"Could not open "
+ filename);
throw e;
}
}
void cleanup() {
try {
file.close();
} catch(IOException e) {
System.err.println(
"file.close()
unsuccessful");
}
}
void countWords() {
try {
while(st.nextToken() !=
StreamTokenizer.TT_EOF) {
String s;
switch(st.ttype) {
case
StreamTokenizer.TT_EOL:
s = new
String("EOL");
break;
case
StreamTokenizer.TT_NUMBER:
s =
Double.toString(st.nval);
break;
case
StreamTokenizer.TT_WORD:
s = st.sval; // Already
a String
break;
default: // single
character in ttype
s =
String.valueOf((char)st.ttype);
}
if(counts.containsKey(s))
((Counter)counts.get(s)).increment();
else
counts.put(s, new
Counter());
}
} catch(IOException e) {
System.err.println(
"st.nextToken()
unsuccessful");
}
}
Collection values() {
return counts.values();
}
Set keySet() { return
counts.keySet(); }
Counter getCounter(String s) {
return (Counter)counts.get(s);
}
public static void main(String[]
args)
throws FileNotFoundException {
WordCount wc =
new WordCount(args[0]);
wc.countWords();
Iterator keys =
wc.keySet().iterator();
while(keys.hasNext()) {
String key =
(String)keys.next();
System.out.println(key +
": "
+
wc.getCounter(key).read());
}
wc.cleanup();
}
} ///:~
Cvičenia
Cv10.1
Do metódy main() v kóde 10.2
pridajte na koniec tieto riadky:
// a este poznamka: pole vlastne neobsahuje objekty
// ale iba odkazy na
ne
Cosi s1 = poleC[1];
System.out.println(s1
+ " " + poleC[1]);
s1.zmenSa();
System.out.println(s1
+ " " + poleC[1]);
poleC[1].zmenSa();
System.out.println(s1
+ " " + poleC[1]);
Preložte, spustite a interpretujte. Na čo si treba dávať pozor pri
práci s poľami?
Cv10.2
Vytvorte triedu SlovoList, ktorá
bude mať tú istú funkcionalitu ako trieda ArrayList, ibaže StringList bude
skladovať a odovzdávať iba objekty triedy Slovo (viď kód P4.1), pri pokuse
uložiť doňho objekt iného typu vyvolá výnimku.
Cv10.3
Pre objekty triedy Slovo vytvorte
SlovoComparator tak, že pri usporiadaní poľa typu Slovo[]
za pomoci tohto komparátora sa
inštancie zoradia odzadu podľa abecedy.
Riešenia a nápoveda
Cv10.1
Pole obsahuje len odkazy na objekty. Ak sa nejakým iným zásahom( než cez príslušný index
poľa ) daný objekt zmeníme, prejaví sa to aj keď ho voláme v poli.
Cv10.3 Aby ste si
zjednodušili prácu, môžete použiť metódu
„int compareTo(object o) “ triedy String.
Vaše komentáre:
kupka@fmph.uniba.sk