autor: Ivan Kupka

Verzia 1

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

 

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.

 

 

 

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Vaše komentáre: kupka@fmph.uniba.sk