⌛⌛⌛ N-ulotteinen taulukko

Aseta vastauksesi tiedostoon NdArray.java kansiossa Round8/ndarray. Muista hakea materiaalit ``student_template_project``sta.

Tässä tehtävässä toteutetaan vakiokokoisen N-ulotteisen taulukon tarjoava geneerinen säiliöluokka NdArray<E>. Ajatus on, että esimerkiksi tapaan new NdArray<String>(2, 5, 4) luotu NdArray-olio toimisi 3-ulotteisena String-taulukkona, jonka ulottuvuuksien koot ovat 2, 5 ja 4. Edellä tarkemmin ottaen ensimmäisen ulottuvuuden koko olisi 2, toisen ulottuvuuden 5 ja kolmannen 4. Vastaavasti esimerkiksi new NdArray<Double>(3, 3, 3, 3, 3) toimisi 5-ulotteisena Double-taulukkona, jonka kaikkien viiden ulottuvuuden koot olisivat 3.

Alussa mainittu taulukon “vakiokokoisuus” viittaa siihen, että rakentimelle annetut parametrit määrittävät taulukon ulottuvuuksien määrän sekä koot, eikä ulottuvuuksien määrää tai kokoja voi myöhemmin muuttaa.

Geneerisellä luokalla NdArray<E> tulee tarkemmin ottaen olla seuraavat julkiset ominaisuudet:

  • Perii Javan luokkakirjaston abstraktin luokan AbstractCollection<E>.

    • Kyseinen luokka edellyttää alempana kuvattujen funktioiden size ja iterator toteuttamisen.

    • Tämän luokan perinnän ansiosta NdArray on pitkälti “täysiverinen” Java-säiliö. NdArray-olion alkioita voidaan esimerkiksi käsitellä streamina hyödyntäen jäsenfunktiota stream().

  • Rakennin NdArray(Integer firstDimLen, Integer ...furtherDimLens), joka ottaa vastaan yhden tai useamman ulottuvuuden koot.

    • Parametrit luettelevat taulukon kunkin ulottuvuuden 1…``N`` koot.

      • firstDimLen kertoo ensimmäisen ulottuvuuden koon, ja furtherDimLens ulottuvuuksien 2…``N`` koot (jos ulottuvuuksia enemmän kuin yksi).

        • Ensimmäisen ulottuvuuden koko otetaan erikseen, jotta rakentimelle olisi pakko antaa vähintään yksi parametri.

      • Eli taulukon ulottuvuuksien määrä N = parametrien lukumäärä = 1 + parametrien furtherDimLens lukumäärä (taulukon koko).

    • Jokaisen ulottuvuuden koon on oltava ei-negatiivinen (eli 0 on kuitenkin sallittu).

      • Jos jonkin ulottuvuuden koko on negatiivinen, heitetään NegativeArraySizeException viestillä “Illegal dimension size dimLen.”, missä dimLen on ensimmäinen rakentimen saama negatiivinen koko.

  • Jäsenfunktio int size(), joka palauttaa taulukon koon (eli sen kaikkien ulottuvuuksien kokojen tulon). Esimerkiksi 3-ulotteisen taulukon, jonka ulottuvuuksien koot ovat 2, 5 ja 4, koko on 2 · 5 · 4 = 20.

  • Jäsenfunktio E get(int... indices), joka palauttaa taulukon parametrien indices kuvaamien indeksien mukaisen alkion.

    • Parametrien indices lukumäärän tulee vastata taulukon ulottuvuuksien määrää N.

      • Jos parametreja on väärä määrä, heitetään IllegalArgumentException viestillä “The array has N dimensions but x indices were given.”, missä N on taulukon ulottuvuuksien määrä ja x on parametrien indices lukumäärä.

    • Kunkin indeksin tulee olla laillinen eli välillä 0…kyseisen ulottuvuuden koko - 1.

      • Jos jollekin ulottuvuudelle annetaan laiton indeksi, heitetään IndexOutOfBoundsException viestillä “Illegal index i for dimension dim of length dimLen."”, missä i on ensimmäinen laiton indeksi parametrien indices joukossa, ja dim sekä dimLen sitä vastaavan ulottuvuuden järjestysnumero ja koko. Ulottuvuuksien järjestysnumerot ovat 1, …, N.

      • Esim. jos on 3-ulotteinen taulukko, jonka ulottuvuuksien koot ovat 2, 5 ja 4, on muotoa get(i, j, k) oleva kutsu laillinen jos ja vain jos 0 ≤ i < 2, 0 ≤ j < 5 ja 0 ≤ k < 4.

  • Jäsenfunktio void set(E item, int... indices), joka asettaa alkion item taulukon parametrien indices kuvaamien indeksien mukaiseen kohtaan.

    • Parametreja indices koskee samat vaatimukset kuin edellä funktiossa get, ja laittomien parametrien tapauksessa heitetään samanlaiset poikkeukset.

  • Jäsenfunktio int[] getDimensions(), joka palauttaa taulukon ulottuvuuksien koot sisältävän taulukon.

    • Palautettavan taulukon koko on N, ja indeksin i kohtaan tallennettu arvo kertoo (``i``+1):nnen ulottuvuuden koon.

    • NdArray ylläpitänee tällaista taulukkoa sisäisesti muistaakseen omien ulottuvuuksiensa koot, mutta huomio: älä palauta suoraan tällaista sisäistä taulukkoa! Luo uusi taulukko ja aseta ulottuvuuksien koot siihen, sillä funktion kutsuja voi muuttaa saamansa taulukon sisältämiä arvoja ja voisi muuten sotkea olion sisäistä dataa.

  • Toteuttaa rajapinnan Iterable<E> (koska peritty AbstractCollection<E> toteuttaa sen).

    • Käy läpi taulukon alkiot samassa järjestyksessä kuin tyypillinen N:stä sisäkkäisestä silmukasta koostuva tapa, jossa uloin silmukka iteroi ensimmäistä ulottuvuutta, sen sisällä oleva silmukka toista ulottuvuutta, jne.

    • Määritä sopiva rajapinnan Iterator<E> toteuttava iteraattoriluokka (esim. yksityisenä sisäisena luokkana).

      • Jos noudatat (ja kannattaa noudattaa) alla annettua ohjetta, iteraattori on mahdollisesti hyvinkin yksinkertainen: ylläpitää vain tietoa siitä, missä kohtaa yksiulotteista taulukkoa iterointi tällä hetkellä etenee.

    • Toteuta funktio Iterator<E> iterator(), joka luo ja palauttaa edellä viitatun iteraattoriluokkasi toteuttaman iteraattoriolion.

Ohje: eräs suhteellisen yksinkertainen ja yleinen lähestymistapa N-ulotteisen taulukon tallentamiseen on tallettaa alkiot tosiasiassa yksiulotteiseen taulukkoon. Tässä suurin haaste on toteuttaa kuvaus moniulotteisen taulukon indekseistä yksiulotteisen taulukon indeksiksi. Idea on kuvattu esim. tässä artikkelissa (tässä kannattaa noudattaa artikkelissa ensimmäiseksi kuvattua “row-major” järjestystä, jolloin iterointi on triviaalia). En suosittele yrittämään “aidosti” moniulotteista ratkaisua; se monimutkaistaisi ratkaisua oleellisesti.

Toteutuksen testaus

Voit testata luokkiasi tiedostossa NdArrayTest.java annetun valmiin testiohjelman ja tiedostoissa output1.txt, output2.txt, output3.txt ja output4.txt annettujen esimerkkitulosteiden avulla. Aseta nämä tiedostot sekä omat luokkatoteutuksesi samaan hakemistoon, käännä ohjelma esim. tapaan javac *.java, ja suorita testit tapaan java NdArrayTest 1, java NdArrayTest 2, java NdArrayTest 3 ja java NdArrayTest 4. Suoritusten pitäisi tuottaa tiedostoissa output1.txt, output2.txt, output3.txt ja output4.txt kuvatut tulosteet.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.