Liigu peamise sisu juurde

Sissejuhatus kollektsioonidesse

Sissejuhatus

Kollektsioon on andmekogum, mis võimaldab hoida ja hallata mitut sama tüüpi objekti (nagu järjend, hulk või sõnastik). Hoolimata massiivide sarnastest kasutusviisidest, on need kollektsioonidega võrreldes jäigemad ja vähemate võimalustega.

Massiivid on fikseeritud suurusega ning puuduvad sisseehitatud meetodid erinevate operatsioonide läbi viimiseks. Nende kitsaskohtade lahendamiseks on loodud Collections raamistik.

Kollektsioonide suurus mälus muutub automaatselt – vastavalt vajadusele see kasvab või kahaneb. Iga kollektsiooni tüübi jaoks on olemas mitu implementatsiooni, mis on loodud konkreetsete ülesannete paremaks lahendamiseks. (Näiteks ArrayList sobib kõige paremini andmete kiireks leidmiseks, aga LinkedList on mõistlik kasutada siis, kui nimekirja on vaja tihti muuta.)

Lisaks on kollektsioonides sisseehitatud meetodid, mis muudavad elementide lisamise, eemaldamise ja sorteerimise lihtsaks ning mugavaks.

Peamised tüübid

Javas on peamiselt nelja tüüpi kollektsioone. Igaüks nendest on mõeldud teatud juhtudeks:

TüüpNimetusKirjeldusPeamine omadus
ListJärjendElementide järjestatud nimekiri.Lubab kordusi (duplikaate).
SetHulkJärjestamata kogum unikaalsetest elementidest.Korduvad elemendid on keelatud.
MapKujutisHoiab andmeid võti-väärtus paaridena.Võtmed on unikaalsed.
QueueJärjekordElementide kogum, kus kehtib järjekorra põhimõte.FIFO - elemendid eemaldatakse samas järjekorras, nagu nad lisati (esimesena sisse, esimesena välja).

Kollektsioonide importimine

Kollektsioonid kuuluvad java.util paki alla ning nende kasutamiseks tuleb need esmalt faili importida:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

// Or import all at once:
import java.util.*;
MÄRKUS

Kuigi viimast import-lauset on mugav kasutada, siis üldiselt .*-iga importimist reeglina välditakse.

Selleks on paar põhjust:

  • Raskem koodiloetavus – pole koheselt ilmselge, milliseid klasse failis tegelikult kasutatakse.
  • Nimeruumikonfliktid – tekkida võivad klassinimede kokkupõrked teiste pakettidega. - Näide: Nii java.util kui ka java.sql paki sees on klass nimega Date. Kui mõlemast pakist imporditakse kõik klassid (*), tekib kompileerimisviga.

<Type> süntaks

Kollektsioone luues tuleb märkida, mis tüüpi objekte need endas hoidma hakkavad. Seda tehakse noolsulgi (<, >) kasutades.

Selle eeliseks on:

  • Tüübikindlus (Type safety) – kompilaator takistab valet tüüpi elementide lisamist kollektsiooni.
  • Väldib tüübiteisendust (Type casting) – Kui tüüp on noolsulgudes määratud, teab arvuti täpselt, mis objektid listis on. Sa ei pea andmeid kollektsioonist välja võttes enam käsitsi "õigesse tüüpi" muutma, mis vähendab võimalusi vigade tekkimiseks.

Näiteks:

ArrayList<String> words = new ArrayList<>();      // Holds String objects
ArrayList<Integer> numbers = new ArrayList<>(); // Holds Integer objects
HashMap<String, Double> prices = new HashMap<>(); // Keys are String, values are Double

words.add("Alice");
String name = words.get(0); // No casting needed

words.add(42); // Compile error - cannot add Integer to String list

Üldisemalt noolsulud on seotud geneeriliste tüüpide (generics) teemaga, mille kohta tuleb eraldi artikkel tulevikus.

Primitiivsed tüübid kollektsioonides

Java kollektsioonid suudavad hoiustada ainult objekte. Primitiivisete andmetüüpe (nagu int, char või boolean) otse kollektsiooni lisada ei saa – see põhjustaks kompileerimisvea. Selle asemel tuleb kasutada vastavaid mähisklasse (nt int asemel Integer)

ArrayList<int> numbers;        // Compile error
ArrayList<Integer> numbers; // Correct

ArrayList<Integer> values = new ArrayList<>();
values.add(10); // int automatically converted to Integer
int x = values.get(0); // Integer automatically converted to int

Seda protsessi, kus Java teisendab primitiivtüübid automaatselt nende vastavateks mähisklassideks ja vastupidi, nimetatakse automaatseks pakendamiseks ja lahtipakendamiseks (autoboxing ja unboxing). Põhjalikuma selgituse ja näited selle toimimise kohta leiad sissejuhatus andmetüüpidesse peatükist.

Kollektsioonide hierarhia

Java Collections raamistik on ehitatud hierarhiliselt ehk see koosneb liidestest ning nende implementatsioonidest.

Collection liides

Enamus andmekogutüüpe implementeerivad Collection liidest. Liideste kohta tuleb tulevikus eraldi peatükk, kuid lühidalt on need nagu lepingud, mis defineerivad endas ära meetodeid, mida täideviiv (implementeeriv) klass peab täitma.

Näiteks List liides kirjeldab endas järgmisi meetodeid:

  • add(T element) - Lisab elemendi järjendisse.
  • get(int index) - Tagastab elemendi antud indeksil.
  • remove(int index) - Eemaldab järjendist elemendi antud indeksil.

Nii ArrayList kui ka LinkedList implementeerivad List liidest, kuid nende sisemine loogika on erinev.

Collection-liides paneb paika ühtsed reeglid elementide töötlemiseks. Kujutised (Maps) on siinkohal erandid – neil on spetsiifiline Map-liides, kuid tavapäraselt kategoriseeritakse ka neid kollektsioonideks. Kollektsioonide hierarhia on järgmine (näited ja implementatsioonid ei piirdu nendega, mis siin loetletud on):

  • Collection (interface)
    • List (interface)
      • ArrayList (implementation)
      • LinkedList (implementation)
    • Set (inteface)
      • HashSet (implementation)
      • TreeSet (implementation)
      • LinkedHashSet (implementation)
    • Queue (interface)
      • LinkedList (implementation)
      • PriorityQueue (implementation)
      • ArrayDeque (implementation)
  • Map (interface)
    • HashMap (implementation)
    • TreeMap (implementation)
    • LinkedHashMap (implementation)

Erinevad implementatsioonid

Miks sedasi? Igal implementatsioonil on oma kindel eesmärk, näiteks:

Valik ArrayList-i ja LinkedList-i vahel sõltub otstarbest:

  • ArrayList on eelistatud siis, kui on vaja kiiret ligipääsu elementidele indeksi abil või kui elemente lisatakse peamiselt nimekirja lõppu. Keskelt lisamine või eemaldamine on aga aeglasem.
  • LinkedList on sobivam juhul, kui elemente lisatakse või eemaldatakse tihti nimekirja algusest või keskelt.

Põhjalikum ülevaade nendest erinevustest on välja toodud järgmistes alampeatükkides.

Programmeerimine liideste kaudu

Programmeerides on soovitatav määrata muutuja tüübiks liides – see muudab koodi paindlikumaks ja kergemini muudetavaks.

// Good - using interface type
List<String> names = new ArrayList<>();

// Works, but less flexible
ArrayList<String> names = new ArrayList<>();

See lähenemine võimaldab hiljem lihtsamini vahetada implementatsiooni.

// Easy to change from ArrayList to LinkedList
List<String> names = new ArrayList<>();

// Later, if needed:
List<String> names = new LinkedList<>();

// Code using 'names' doesn't need to change