11. díl - ArrayList v Javě

Java Objektově orientované programování ArrayList v Javě

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém dílu tutoriálů o Javě jsme si řekli něco o datu a času. Dnes si ukážeme jednu kolekci, která je chytřejší, než pole. Umožňuje totiž prvky libovolně přidávat a mazat.

Pojem kolekce jsme tu již zmínili. Je to struktura, do které můžeme ukládat více objektů. Kolekcí je v Javě velké množství, jsou uzpůsobeny na různé účely a můžeme s nimi zacházet různými způsoby. Proto jim je věnována celá sekce. Dosud známe pouze kolekci pole. V průběhu seriálu však budeme potřebovat něco chytřejšího, kam budeme moci jednoduše za běhu programu přidávat a mazat záznamy. Jistě by se nám hodilo si v paměti spravovat databázi nějakých objektů. Víme, že pole má konstantní velikost, což je daň za jeho vysokou rychlost. Nyní si představíme ArrayList, který můžeme již podle názvu chápat jako nadstavbu pole.

ArrayList

ArrayList je tzv. generická kolekce. Pojem genericita si plně vysvětlíme až u kolekcí, nyní nám bude stačit vědět, že při deklaraci ArrayListu specifikujeme datový typ objektů, které v něm budou uloženy. Začněme jednoduše a udělejme si ArrayList čísel, která budeme náhodně losovat.

Losování

Program se nás vždy zeptá, zda chceme losovat další číslo a to se přidá do ArrayListu. Pokud již nebudeme chtít losovat, program vypíše losovaná čísla, seřazená od nejmenšího po největší. Založme si nový projekt Losovani a vytvořme si třídu Losovac. Třída bude obsahovat ArrayList typu Integer, kde budou čísla uložena.

Narážíme na třídu Integer, která slouží k uložení celých čísel a v podstatě obaluje typ int. Do ArrayListu se totiž dají vkládat pouze objekty, tedy proměnné referenčního typu. Pro tyto účely existuje typ Integer. Ke každému primitivnímu typu v Javě existuje jeho referenční "obal", brzy si je uvedeme. ArrayList bude privátní a bude sloužit pouze jako interní úložiště dané třídy, aby se na něj zvenku nedalo přistupovat.

ArrayList deklarujeme takto:

ArrayList<Integer> cisla;

Datový typ píšeme u generických kolekcí do špičatých závorek. ArrayList je samozřejmě objekt, jako každý jiný. Stejně jako u pole a jiných objektů, i zde proměnnou před použitím inicializujeme:

ArrayList<Integer> cisla = new ArrayList<Integer>();

Všimněte si závorek, které značí konstruktor. Takovýto list tedy umístíme do naší třídy, spolu s náhodným generátorem Random. Pro práci s třídou ArrayList je třeba přidat import java.util.Arra­yList. V konstruktoru atributy inicializujeme:

import java.util.ArrayList;
import java.util.Random;

public class Losovac {
        private ArrayList<Integer> cisla;
        private Random random;

        public Losovac() {
                random = new Random();
                cisla = new ArrayList<Integer>();
        }

}

Dále přidáme metody losuj() a vypis(), kde losuj() přidá do ArrayListu nové náhodné číslo a také ho vrátí jako návratovou hodnotu. vypis() vrátí textový řetězec pro vypsání. Ten bude obsahovat čísla z cisla, seřazená a oddělená mezerou.

Losování náhodného čísla již známe z dílu o hrací kostce, zde budeme vyhazovat čísla od 1 do 100. Číslo do ArrayListu přidáme pomocí metody add():

public int losuj() {
        Integer cislo = random.nextInt(100) + 1;
        cisla.add(cislo);
        return cislo;
}

Velmi jednoduché, že? Kolekce ArrayList je interně poměrně složitá a zatím se nebudeme zabývat tím, co se uvnitř děje. To je ostatně účel Javy, nabízet kvalitní a sofistikované komponenty, které se jednoduše používají.

Výpis čísel bude ještě snazší. K setřídění ArrayListu použijeme metodu sort ze třídy Collections, která list setřídí. Bude tedy potřeba import java.util.Collec­tions. Metoda nic nevrací, pouze ArrayList setřídí uvnitř.

public String vypis() {
        String s = "";
        Collections.sort(cisla);
        for (int i : cisla) {
                s += i + " ";
        }
        return s;
}

Hotovo.

Přesuňme se do main() a pomocí while cyklu umožněme uživateli ovládat objekt. Podobný program byla kalkulačka z prvních lekcích, kde jsme se v cyklu ptali, zda si uživatel přeje opakovat výpočet. Zde budeme postupovat totožně.

Ovládání bude pomocí možností 1, 2, 3 (losuj, vypiš, konec). Budeme je načítat pomocí sc.nextLine() jako String.

Scanner sc = new Scanner(System.in, "Windows-1250");
Losovac losovac = new Losovac();
System.out.println("Vítejte v programu losování.");
String volba = "0";
// hlavní cyklus
while (!(volba.equals("3"))) {
    // výpis možností
    System.out.println("1 - Losovat další číslo");
    System.out.println("2 - Vypsat čísla");
    System.out.println("3 - Konec");
    volba = sc.nextLine();
    System.out.println();
    // reakce na volbu
    switch (volba) {
        case "1":
            System.out.printf("Padlo číslo: %s\n", losovac.losuj());
            break;
        case "2":
            System.out.printf("Padla čísla: %s\n", losovac.vypis());
            break;
        case "3":
            System.out.println("Děkuji za použití programu");
            break;
        default:
            System.out.println("Neplatná volba, zadejte prosím znovu.");
            break;
    }
}

Nezapomeneme na import Scanner. Vlastně můžeme nechat řešení importů na NetBeans, stačí kliknout pravým tlačítkem a zvolit Fix imports, případně použít klávesovou zkratku Ctrl + Shift + I.

Průběh programu je z kódu dobře viditelný, nejprve nastavíme volbu na nějakou výchozí hodnotu, aby cyklus poprvé proběhl. Poté volbu načteme z klávesnice. String zpracujeme pomocí switche a provedeme příslušné akce. Pokud bylo zadáno něco jiného, pokryje to možnost default.

Losovač náhodných čísel v Javě pomocí Listu

Vidíme, že můžeme stále přidávat nová a nová čísla. Máme mnohem větší možnosti, než s polem. Zároveň však můžeme s ArrayListem pracovat podobně, jako jsme pracovali s polem.

K přístupu můžeme využít metody get() a set(), ale pozor, prvek musí existovat. Zkusme si napsat následující kód:

ArrayList<String> l = new ArrayList<String>();
l.add("První");
System.out.println(l.get(0));
l.set(0, "První položka");
System.out.println(l.get(0));
l.set(1, "Druhá položka");  // vyhodí chybu

Vytvoříme si list Stringů. Přidáme položku "První" a poté vypíšeme položku na indexu 0. Vypíše se nám "První". Můžeme na ni samozřejmě i takto zapisovat. S druhou položkou na pozici 1 však již nemůžeme pracovat, protože jsme ji do listu nepřidali. U pole jsme zadali velikost a on všechny "přihrádky" (proměnné pod indexy) založil. Nyní velikost nezadáváme a "přihrádky" si přidáváme sami.

Podívejme se na ArrayList podrobněji a vypišme si metody, které jsou pro nás nyní zajímavé:

Konstruktory

Kromě prázdného ArrayListu můžeme List vytvořit i jako kopii z jiného listu, pole nebo jiné kolekce. Stačí kolekci předat do konstruktoru:

String[] poleStringu = {"První", "Druha", "Třetí"};
List<String> l = new ArrayList<String>(Arrays.asList(poleStringu));
System.out.println(l.get(2));

Kód výše vypíše "Třetí". Prvky pole se do nového listu zkopírují. Stejně můžeme předat i jiný ArrayList.

Metody na ArrayListu

  • size() - Funguje jako length na poli, vrací počet prvků v kolekci.
  • add(položka) - Metodu add() jsme si již vyzkoušeli, jako parametr bere položku, kterou vloží do listu.
  • addAll(kolekce) - Přidá do listu více položek, např. z pole.
  • clear() - Vymaže všechny položky v listu.
  • contains(položka) - Vrací true/false podle toho, zda ArrayList obsahuje předanou položku.
  • indexOf(položka) - Vrátí index prvního výskytu položky (jako u pole). Vrací -1 při neúspěchu.
  • lastIndexOf(po­ložka) - Vrací index posledního výskytu položky v Listu. Vrací -1 při neúspěchu.
  • remove(položka) - Vymaže první nalezenou položku.
  • removeAll(index, počet) - Vymaže daný počet prvků od zadaného indexu.
  • toArray() - Zkopíruje položky z ArrayListu do pole a to vrátí.

Další metody

Další metody a pro práci s listem najdeme ve třídě Collections. Jako parametr berou danou kolekci.

  • min() - Vrátí nejmenší prvek z kolekce.
  • max() - Vrátí největší prvek z kolekce.
  • reverse() - Obrátí list tak, že je první položka poslední a naopak. Metoda nic nevrací, změny se provedou v zadaném listu.
  • sort() - Sort již také známe, setřídí položky v listu. Metoda opět nic nevrací.

Vidíme, že kolekce ArrayList toho umí mnohem více, než pole. Největší výhodou je přidávání a mazání prvků. Daň ve výkonu je zanedbatelná. V sekci s kolekcemi zjistíme, že ArrayList má ještě další metody, ale zatím na to nemáme zkušenosti.

Program pro ukládání losovaných čísel byl zajímavý, ale jistě se nám bude v budoucnu hodit ukládat spíše plnohodnotné objekty, než čísla. Příště si uděláme pomocí ArrayListu databázi, bude to elektronický diář! :)


 

Stáhnout

Staženo 535x (22.32 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (12 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (31)

Avatar
Libor Šimo (libcosenior):

Stiahol si si vzorovy kod pod clankom a porovnal s tym svojim?

Editováno 14.12.2016 18:12
Odpovědět 14.12.2016 18:11
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Vít Pelc
Člen
Avatar
Vít Pelc:

Hlavní třída s metodou main

package arraylist;


import java.util.Scanner;
public class Arraylist {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Losovac l = new Losovac();
        String volba="0";

        while(!(volba.equals("3")))
        {
        System.out.println("Co si přejete provést za akci?: ");
        System.out.println("1. : Losovat číslo ");
        System.out.println("2. : Vypsat čísla ");
        System.out.println("3. : Konec ");
        volba = sc.nextLine();
        System.out.println();
        switch (volba)
            {
            case "1":
                 System.out.printf("Padlo číslo: %s\n ", l.losuj());
                break;
            case "2":
                System.out.printf("Padla čísla: %s\n ", l.vypis());
                break;
            case "3":
                System.out.println("Děkuji za použití programu.");
                break;
            default:
                System.out.println("Špatná volba!");
                break;
            }
        System.out.println();
        }
    }

}

třída Losovač

package arraylist;


import java.util.ArrayList;
import java.util.Random;
import java.util.Collections;

public class Losovac {
    private ArrayList<Integer> cisla;
    private Random random;

    private void Losovac()
    {
     this.random = new Random();
    this.cisla = new ArrayList<Integer>();
    }

    public int losuj()
    {
        int cislo = random.nextInt(99)+1;
        cisla.add(cislo);
        return cislo;
    }

    public String vypis()
    {
        String s = " ";
        Collections.sort(cisla);
        for (int i : cisla)
        {
            s += i + " ";
        }
        return s;
    }

}
 
Odpovědět 20.12.2016 17:30
Avatar
Odpovídá na Vít Pelc
Petr Štechmüller:

Ahoj, z jakého důvodu máš ve třídě Losovac privátní konstruktor? To by asi byt nemělo...

edit: ty vůbec nemáš definovány konstruktor

Editováno 20.12.2016 18:31
Odpovědět  +1 20.12.2016 18:31
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Vít Pelc
Člen
Avatar
Odpovídá na Petr Štechmüller
Vít Pelc:

Nojo, toho privátního konstruktoru jsem si ani nevšiml. Díky za upozornění. To byla ta zásadní chyba, teď už to jede jako na drátkách :). Chyba z nepozornosti, ještě se mám co učit.

 
Odpovědět 23.12.2016 19:18
Avatar
Odpovídá na Vít Pelc
Petr Štechmüller:

Jenže chyba byla někde jinde. Ty jsi měl definovanou metodu takto:

private void Losovac() {}

problém byl v tom, že tohle není konstruktor, ale obyčejná privátní metoda bez návratové hodnoty.

Odpovědět 23.12.2016 19:23
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Vít Pelc
Člen
Avatar
Odpovídá na Petr Štechmüller
Vít Pelc:

Jo, já vím. Už ani nevím, proč jsem to tam tak pitomě napsal :D. Byl jsem asi myšlenkama někde jinde.
Každopádně už mám konstruktor definovaný správně, a to:

public Losovac() { }
Editováno 23.12.2016 19:25
 
Odpovědět  +1 23.12.2016 19:25
Avatar
Mirka Baranová:
  1. otázka: Je metoda: removeAll(index, počet) - "Vymaže daný počet prvků od zadaného indexu." správně? Mi to funguje jen takto: removeAll(kolekce) - vymaže celou kolekci z listu.
  2. otázka: Jak se používá metoda toArray()? Když si založím ArrayList "rodina" jako list jmen a nechám si vypsat System.out.prin­tln(rodina.to­Array()); výstup je: [Ljava.lang.Ob­ject;@15db9742
Odpovědět 15. ledna 22:07
Pro člověka, který je plný touhy, není nic nemožné.
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Mirka Baranová
pocitac770:
  1. Zkoušel jsem něco k tomu dohledat, nic jsem nenašel, vypadá to na chybu ve článku. Možná autor myslel na metodu .subList(), která má stejné parametry, ale vytváří nový list, obsahující právě tyto prvky. .removeAll() opravdu funguje jak popisuješ.
  2. .toArray() vrací pole, a to doslava, to, co se snažíš je vypsání reprezentace pole, a základní Stringová reprezentace je co? Hash. Viz přetěžování metod, .toString apod. Je to to samé, jako bys napsala
ArrayList<Neco> a = nevimCoMamTedSakraVymysletJePozdeVecer();
Neco[] pole = a.toArray();
System.out.pritnln(pole); //špatně
System.out.pritnln(Arrays.toString(pole); //správně
Editováno 16. ledna 0:11
 
Odpovědět 16. ledna 0:10
Avatar
Mirka Baranová:

2. Jo, chápu, ale mi šlo o to, že mi nejdou do pole přidat prvky z toho ArrayListu. V tvém příkladu 2. řádek nebo můj příklad:

ArrayList<String> rodina = new ArrayList<String>();
rodina.add("Alena");
rodina.add("Petr");
String[] jmena = rodina.toArray(); // tohle nefunguje
Odpovědět 21. ledna 18:44
Pro člověka, který je plný touhy, není nic nemožné.
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Mirka Baranová
pocitac770:

Tak jsem to vyzkoušel, problém je ten, že to vrací pole výhradně Objectů. To znamená, že to jde vyřešit dvěmi cestami...

  1. Postupně jednotlivé Objecty protypovávat na Stringy, pracovat s nimi, a potenciálně je neskládat do nového pole
  2. Použít v metodě jako parametr pole, které má stený typ (v tomto případě String), pokud bude délka stejná, jako délka listu, pak se rovnou do něj všechny prvky doplní, v opačném případě si to pouze vezme typ a vrátí nové pole. Toho jde využít, když máš již nějaké jiné pole vytvořené a chceš ho znovu použít na toto, pak můžeš doplnit do parametru přímo instanci a vynechat "doplňovací" část před rovnítkem
ArrayList<String> list = new ArrayList<String>();
list.add("Jedna");
list.add("Dva");
//1)
Object[] pole1 = list.toArray();
Sring[] pole2 = new String[pole1.length];
for(int i = 0; i < pole1.length; i++){
        System.out.println((String)pole1[i]);
        pole2[i] = (String)pole1[i];
}
//2.1)
String[] pole3 = list.toArray(new String[list.size()]);
for(String s : pole3){
        System.out.println(s);
}
//2.2)
String[] pole4 = new String[list.size()];
list.toArray(pole4);
for(String s : pole4){
        System.out.println(s);
}
Editováno 21. ledna 20:04
 
Odpovědět 21. ledna 20:03
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 41. Zobrazit vše