Java (9) - Pole

Minule jsme si ukázali, jak implementovat některé matematické operace. Tyto operace měly tu vlastnost, že vracely jednu návratovou hodnotu, což ovšem není vůbec podmínkou. Dnes si ukážeme, jakým způsobem můžeme vracet více proměnných stejného typu, aniž bychom pro ně museli vytvářet speciální návratový objekt.

Struktura pole, o které budeme mluvit, není samozřejmě určena pouze pro návratové hodnoty. Její využití je především v ukládání vícero hodnot (objektů) pod jednu proměnnou.

Představme si třeba řešičku hlavolamu Sudoku. Bylo velmi nevhodné mít 81 proměnných – pro každé políčko jednu. V případě variabilní velikosti zadání by to tímto způsobem ani nešlo realizovat.

Co je to pole?

Už jsme si řekli, k čemu pole slouží. Nyní si řekneme, co to je. Pole je skupina proměnných stejného typu, které jsou v paměti alokovány za sebou (tj. bez mezer). K těmto proměnným můžeme přistupovat pomocí jejich indexů (pořadí v této posloupnosti, počítáno od 0). Z tohoto způsobu alokace je zřejmé, že pole má fixní délku, kterou nemůžeme nijak změnit (protože paměť v oblasti za polem může být obsazena jiným objektem).

V Javě lze vytvořit pouze pole primitivních datových typů a referencí na objekty, nikoliv však pole objektů samotných. Tato skutečnost není nijak limitující, pouze to znamená, že zatímco jsou reference umístěny jedna za druhou, tak samotné objekty mohou být roztroušeny napříč pamětí.

Technickou poznámkou určenou především pro ty, kteří znají jazyky C a C++ je, že v Javě nelze překročit meze pole (které jsou hlídány virtuálním strojem), v případě použití vyššího indexu, než je velikost pole, program havaruje na výjimce ArrayIndexOutOfBoundsException.

Inicializace

V okamžiku, kdy pole vytvoříme, tak jsou jeho hodnoty přednastaveny do výchozího stavu. V případě celočíselných typů se jedná o hodnotu 0, u typu boolean o hodnotu false, a v případě referencí o ukazatel do prázdna null.

Jednorozměrné pole

Při deklaraci pole nejprve uvedeme typ hodnot, které do něj budeme ukládat, za něj pár hranatých závorek (označení, že se jedná o pole) a název proměnné tohoto pole. Při inicializaci za rovnítko vepíšeme klíčové slovo new následované typem hodnoty, která bude v poli uložena a v hranatých závorkách uzavřenou celočíselnou hodnotu, jež definuje, jak bude pole velké.

        //pole osmi integeru
        int[] array = new int[8];
        
        //pole ctyr retezcu
        String[] stringArray = new String[4];

Pro přístup k hodnotám uloženým v poli používáme hranaté závorky vepsané za jméno proměnné daného pole. V těchto závorkách uvádíme index (pořadí počítané od nuly) zvolené hodnoty.

        //pole osmi integeru
        int[] array = new int[8];
        array[0] = 5; //prvni hodnota je 5
        array[1] = 3; //druha je 3
        array[2] = array[1]; //do treti priradime hodnotu na indexu 1, tj. 3
        
        array[1] = 4; //array[1] == 4, array[2] == 3 (V minulem prikazu jsme pouze priradili hodnotu. Nerekli jsme, ze navzdy budou tyto hodnoty totozne)
        
        //pole ctyr retezcu
        String[] stringArray = new String[4];
        stringArray[0] = "Skakal";
        stringArray[1] = "pes";
        stringArray[2] = "pres";
        stringArray[3] = "oves";
        System.out.println(stringArray[0]); //skakal

Takováto inicializace hodnotami je velmi zdlouhavá a nešikovná. Proto máme při deklaraci s inicializací možnost vytvořit pole alternativním způsobem, kdy na pravou stranu přiřazení napíšeme do složených závorek čárkami oddělené hodnoty, na něž má být pole na jednotlivých indexech inicializováno.

String[] stringArray2 = {"Skakal", "pes", "pres", "oves"}; //pole ctyr retezcu

Příklad

Kvadratická rovnice může 0 až 2 řešení v oboru reálných čísel v závislosti na tom, jesli je diskriminant záporný, nulový nebo kladný.

    /**
     * Resi kvadratickou rovnici o jedne nezname ve tvaru
     * ax^2 + bx + c = 0
     * @param a
     * @param b
     * @param c
     * @return pole realnych korenu
     */
    public static double[] solveQuadraticEquation(double a, double b, double c) {
        double d = b*b - 4*a*c; //diskriminant
        if (d < 0) {
            double[] result = new double[0];
            return result;
        } else if (d == 0) {
            double[] result = {-b/2*a};
            return result;
        } else {
            double[] result = {(-b + Math.sqrt(d))/(2*a), (-b - Math.sqrt(d))/(2*a)};
            return result;
        }
    }

For-each cyklus

Pro průchod přes primitiva a objekty uložené v polích a dalších kolekcích Java obsahuje for-each cyklus, který iteruje přes všechny entity uložené v dané struktuře.

Struktura for-each smyčky je podobná for cyklu, s tím rozdílem, že v závorkách nalezneme nejprve typ proměnné, která je uložena v kolekci, přes níž iterujeme. Za typem následuje název proměnné, pod níž budou v jednotlivých iteracích zpřístupněny uložené objekty, dvojtečka a název proměnné procházené kolekce.


Příklad

Metoda vypisující všechny prvky pole řetězců (bez ohledu na jeho délku) může být zapsána takto:

    /**
     * Vypise retezce ulozene v danem poli
     * @param array pole
     */
    public static void print(String[] array){
        for (String s : array) {
            System.out.println(s);
        }
    }

Příklad 2

Pro výpis prvků pole můžeme také použít klasický for cyklus a proměnnou length, která specifikuje délku, na níž bylo pole inicializováno. Proměnnou length nám poskytuje objekt pole.

    /**
     * Vypise cela cisla ulozena v danem poli
     * @param array pole
     */    
    public static void print(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);          
        }
    }

Všimněme si, že obě metody print mají stejnou signaturu (tzn. návratový typ a jméno metody). V okamžiku překladu na základě předávané hodnoty kompilátor rozhodne, která z metod se má použít. Tomuto principu se říká přetěžování metod (method overloading) a setkali jsme se s ním už mnohokrát při volání metody System.out.println, která je implementována pro všechny primitivní typy (a pro objekty obecně).

Vícerozměrné pole

Vícerozměrné pole – pole polí – použijeme potřebujeme-li postihnout vícerozměrnost dat, která popisujeme. Příkladem je již zmíněné Sudoku.

Při deklaraci vícerozměrných polí postupujeme obdobně jako u pole jednorozměrného, jediným rozdílem je počet závorek, které uvádíme (co dvojice závorek – to jeden rozměr). Na pravé straně přiřazení vždy musíme uvést velikost prvního rozměru (samotného pole polí). Velikost ostatních rozměrů vyplnit nemusíme, ale Java pak tyto rozměry nezinicializuje (to musíme udělat ručně).

Neuvedení dalších rozměrů se může hodit, pokud chceme vytvořit nějakým způsobem nepravidelnou matici – například ve tvaru trojúhelníku.

Pro vícerozměrná pole platí následující poučka: dvojrozměrné pole je časté, trojrozměrné je neobvyklé a více jak trojrozměrné je obvykle chyba návrhu aplikace (jenom málokdo si umí taková data představit).

        //pole 3 radky, 4 sloupce
        int[][] array2d = new int[3][4];

        int[][] array2d2 = new int[3][]; //druhy rozmer neuvadime
        for(int i = 0; i < array2d2.length; i++){ //trojuhelnikova matice
            array2d2[i] = new int[i + 1];
        }

Metoda pro výpis dvojrozměrného pole:

    /**
     * Vypise matici (dvojrozmerne pole)
     * @param array matice
     */
    public static void print(int[][] array) {
        for (int i = 0; i < array.length; i++) { //pruchod pres pole poli
            for (int j = 0; j < array[i].length; j++) { //pruchod samotnym polem (radkem)
                System.out.print(array[i][j] + " "); //bez odradkovani
            }
            System.out.println(""); //odradkovani
        }
    }

Deklaraci s inicializací lze provést rovněž zjednodušeným způsobem stejně u jednorozměrného pole. Jediný rozdíl je v tom, že vytváříme pole polí.

        int[][] setting = { //Zadani Sudoku - MF DNES 23.8. 2010, priloha leto
            {0, 0, 4, 0, 3, 6, 9, 2, 7},
            {1, 0, 0, 0, 0, 5, 0, 0, 0},
            {0, 0, 0, 2, 0, 0, 0, 0, 4},
            {0, 0, 5, 0, 0, 0, 0, 6, 0},
            {6, 4, 0, 0, 0, 0, 0, 8, 5},
            {0, 7, 0, 0, 0, 0, 2, 0, 0},
            {5, 0, 0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 7, 0, 0, 0, 0, 2},
            {4, 3, 7, 9, 2, 0, 5, 0, 0}
        };

Bubble sort

Nyní si ukážeme první skutečný algoritmus – Bubble sort – který slouží k seřazení hodnot. Bubble sort je jedním ze tří základních řadících algorimů (Bubble sort, Selection sort, Insertion sort) a je patrně tím nejčastějším způsobem, který laika napadne, když dostane za úkol seřadit pole (pro porovnání si přečtěte články o ostatních algoritmech).

Princip

Analogií totoho algoritmu jsou různě velké bublinky ve vodě. Ty větší stoupají rychleji než ty menší a proto jako první vystoupá ta největší bublinka, pak druhá největší atd.

Bubble sort v každém svém kroku porovnává dva sousední prvky, a pokud je ten větší zařazen vlevo od menšího, tak je prohodí a pokračuje na dalším indexu, kde postup opakuje. Proto pokud byl první porovnávaný prvek největší (nejlehčí), tak se probublá až na konec pole. Pokud byl v poli nějaký jiný lehčí, tak se na konec pole probublá on. Tak či tak, po prvním průchodu je na konci pole ten správný prvek. Nyní již zbývá pouze tento postup n-1 krát (poslední prvek je seřazen triviálně) zopakovat na redukovaném poli (tj. bez již seřazené části) a celé pole bude seřazeno od nejmenší hodnoty po tu nejvyšší.

    /**
     * Bublinkove razeni
     * @param array pole k serazeni
     */
    public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - i - 1; j++) { //postupne redukujeme problem
                if (array[j] > array[j + 1]) { //pokud je vyssi prvek spatne zarazen
                    int tmp = array[j]; //tak jej prohodime
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                }
            }
        }
    }







Doporučujeme