Razred Vector
V Javini standrardni knjižnici
java.util
najdemo
imeplementacije raznih podatkovnih struktur. V tej lekciji bomo
spoznali
vektorje, to je razred
Vector
.
Kadar v Javi naredimo tabelo z ukazom new
, moram
povedati, kako velika naj bo tabela. Ko je tabela enkrat
narejena, njene velikosti ne moremo spreminjati. To pomeni, da
tabeli ne moremo dodajati in odvzemati elementov.
Kadar potrebujemo podatkovno strukturo, ki je kakor tabela, le
da želimo dodajati in odvzemati elemente, uporabimo razred
Vector
.
Osnovne metode za delo z vektorji in primerjavo s tabelami
prikazuje naslednja razpredelnica:
Osnovne operacije z vektorji |
---|
Vector v = new Vector(); | Naredi nov vektor (velikosti 0). [tabele: Object[] t
= new Object[0]; ]
|
Vector v = new Vector(n); | naredi vektor/tabelo velikosti n [tabele: Object[] t = new Object[n]; ] |
v.size() | velikost vektorja [tabele: t.length ] |
v.elementAt(k) | k -ti element [tabele: t[k] ] |
v.setElementAt(x, k); | nastavi k -ti element na x [tabele: t[k] = x; ] |
v.firstElement(); | prvi element [tabele: t[0] ] |
v.lastElement(); | zadnji element t[t.length - 1] |
v.add(x); | dodaj x na konec vektorja [tabele: ne obstaja] |
v.insertElementAt(x, k); | vrini x na k -to mesto [tabele: ne obstaja] |
v.removeElementAt(k); | odstrani k -ti element [tabele: ne obstaja] |
v.removeAllElements(); | odstrani vse elemente [tabele: t = new Object[0]; ] |
v.removeRange(i, j); | odstrani vse elemente od i -tega do
j -tega [tabele: ne obstaja] |
v.toArray(); | pretvori vektor v tabelo |
v.toString(); | predelaj vektor v niz [tabele: ne obstaja] |
v.contains(x) | ali vektor vsebuje element x ? [tabele: ne obstaja] |
v.indexOf(x) | Indeks, na katerem se pojavi element x [tabele: ne obstaja] |
V zgornji tabeli seveda niso naštete vse metode za delo z
vektorji. Oglejte si še dokumentacijo! Ker ima razred
Vector
metodo toString
, lahko vektorje
izpisujemo z metodo println
.
Kadar želimo napraviti kako operacijo za vsak element tabele
t
, to naredimo s
for
zanko:
for (int i = 0; i < t.length; i++) {
tu nekaj naredimo s t[i]
} |
Enako lahko naredimo z vektorjem
v
:
for (int i = 0; i < v.size(); i++) {
tu nekaj naredimo z v.elementAt(i)
} |
Vendar to ni najboljša rešitev, ker prav v ta namen obstaja
konstrukt, ki se imenuje
iterator. To je objekt, s
pomočjo katerega se sprehodimo čez elemente vektorja. Priporočen
način je torej takšen:
Iterator it = v.iterator();
while (it.hasNext()) {
Object x = it.next();
tu nekaj naredimo z x
} |
Metoda
hasNext()
vrne
true
, če ima iterator na
voljo še kak objekt. Metoda
next()
vrne naslednji
element.
Naloga 1
Napravimo (statično) metodo, ki kot argument dobi ime vhodne datoteke.
Vse vrstice vhodne datoteke prebere v objekt razreda Vector, ki ga vrne
kot vrednost. Elementi naj bodo v vektorju v enakem vrstnem redu kot so
vrstice na vhodni datoteki.
[
rešitev]
Naloga 2
Napravimo (statično) metodo, ki kot argument dobi objekt razreda Vector
in ime izhodne datoteke. Vse elemente vektorja izpiše na datoteko po enega v
vrstico.
[
rešitev]
Naloga 3
Napravimo (statično) metodo, ki kot argument dobi ime vhodne datoteke.
Vse vrstice vhodne datoteke prebere v objekt razreda Vector, ki ga vrne
kot vrednost. Vsako vrstico iz vhodne datoteke vstavimo v vektor tako, da
bodo elementi v vektorju sortirani po abecedi.
Namig
Skelet metode bo enak kot v vaji 1. Namesto dodajanja na
konec vektorja (add()
), bomo izbrali primerno mesto v
vektorju in uporabili insertElementAt
.
[
rešitev]
Naloga 4
Napravimo (statično) metodo, ki kot argument dobi objekt razreda Vector
in ime izhodne datoteke. Vse elemente vektorja izpiše na datoteko.
Elementi iz vektorja naj se izpišejo v abecednem vrstnem redu. Pri tem lahko
vsebimo vektorja spremenimo (na koncu je lahko prazen).
Namig
Najenostavnejša ideja, ki nam pade na pamet je taka:
Dokler vektor ni prazen, ponavljamo
poiščemo najmanjši element v vektorju
najdeni element izpišemo
najdeni element zbrišemo |
Iskanje najmanjšega elementa pa gre takole:
prepostavimo, da je prvi element najmanjši
zapeljemo se čez vse elemente:
če je trenutni element manjši od doslej najmanjšega,
vzamemo trenutni element za nov najmanjši element |
Ker bomo za brisanje potrebovali indeks najmanjšega
elementa, si moramo vsakokrat poleg vrednosti najmanjšega
elementa zapomniti tudi njegov indeks.
[
rešitev]
Naloga 5
Napravimo (statično) metodo, ki kot argument dobi objekt razreda Vector
in ime izhodne datoteke. Vse elemente vektorja izpiše na datoteko.
Elementi iz vektorja naj se izpišejo v abecednem vrstnem redu. Pri tem
mora ostati vsebina vektorja nespremenjena.
Namig
Delovanje metode iz naloge 4 najlažje popravimo tako, da
namesto brisanja elementov iz vektorja le označimo, da je
element zbrisan. To najlažje napravimo s tabelo
boolean[] zbrisan = new boolean[vektor.size()]; |
Ko postavimo
zbrisan[i]
na
true
, naj to
pomeni, da elementa
vektor.elementAt(i)
ne
gledamo več.
Malce moramo premisliti, kako bomo spremenili iskanje
minimuma (težje je predpostaviti, da je prvi element
minimalen - oziroma težje je vedeti, kateri element je
prvi).
[
rešitev]
Naloga 6
Napravimo (statično) metodo, ki kot argument dobi objekt razreda Vector in
vrne isti objekt, le da so elementi v njem sortirani po velikosti.
Namig
Za začetek razmišljajmo kot nadaljevanje naloge 4. Namesto
brisanja najmanjšega elementa, lahko med seboj zamenjamo
prvi element in najmanjši element in nato postopek ponovimo
od drugega elementa naprej... Mislimo si torej, da je tabela
od začetka do nekega indeksa že sortirana. Ko najdemo
najmanjši element nesortiranega dela tabele in ga damo na
začetek nesortiranega dela tabele, smo sortiran del povečali
za en element.
Seveda pa je to le eden od enostajnejših (in neuporabnih) postopkov sortiranja...
[
rešitev]
Naloga 7
Napravimo podobno metodo kot v prvi nalogi, le da vemo, da so na datoteki
podatki sestavljeni iz imena in priimka študenta ter ocene. Podatki na vhodni datoteki
so med seboj ločeni z znaki TAB.
Definirajmo razred Student,
ki bo vseboval vse tri komponente. V razredu Student definirajmo tudi metodi
toString in compareTo (študente med seboj primerja po abecedi).
Statična metoda naj vrne vektor objektov razreda Student.
Namig
Definicija razreda Student
s tremi komponentami ne bi smela biti problem.
Najbrž je najbolje napraviti kar kontruktor objekta Student,
ki dobi tak niz, kot ga pričakujemo na vhodni vrstici.
Ko imamo tak konstruktor, popravimo metodo iz naloge 1 tako,
da namesto vhodnega niza v vektor doda novo konstruiran
objekt iz vhodnega niza.
[
rešitev]
Naloga 8
Enaka nakoga kot naloga 2, le da so v vektorju objekti razreda Student.
Namig
Ker smo pametno pripravili razred Student, je rešitev takorekoč kopija naloge 2.
[
rešitev]
Naloga 9
Enaka naloga kot naloga 3, le da so v vektorju objekti razreda Student.
Namig
Ker smo pametno pripravili razred Student, je rešitev takorekoč kopija naloge 3.
[
rešitev]
Naloga 10
Na osnovi razreda Student napravimo razred StudentR1, ki bo vseboval vse komponente
Studenta, poleg tega pa še oceno na drugem kolokviju. Napišimo statično metodo, ki bo
dobila imeni dveh datotek (prve z rezultati prvega kolokvija in druge z rezultati
drugega kolokvija).
Metoda bo prebrala obe datoteki in vrnila vektor objektov reazreda StudentR1. V vektorju se
lahko vsak student pojavi le enkrat. Upoštevajmo, da se lahko posamezni študent pojavi le v eni
datoteki, tedaj naj ima oceno iz drugega kolokvija enako 0.
Namig
Z določilom extends Student
bomo naredili podrazred.
Dodajanje ene same komponente v razred StudentR1
bo enostavno.
Popraviti bo treba kakšen konstruktor in metodo toString
.
Branje prve datoteke bo skoraj dobesedna kopija branja podatkov iz naloge
7.
Pri branju druge datoteke si lahko pomagamo tako, da uporabljamo kar
enak konstruktor kot za prvo branje. Če novo prebranega študenta še ni v
tabeli, le preselimo prvo oceno v drugo. Če pa smo študenta že našli v tabeli,
pa preselimo prvo oceno novega studenta v drugo oceno starega. Ker komponent
razreda Student nismo napravili javnih, je treba taki metodi napraviti
v StudentR1
.
Pri iskanju, ali je študent že v tabeli, si lahko pomagamo s metodo
indexOf
, ki nam vrne indeks enakega elementa v vektorju ali -1.
Primerjavo dela z metodo equals
, kar nam bo zelo ustrezalo.
[
rešitev]
Naloga 11
Metodo iz naloge 8 popravimo na razred StudentR1. Izpiše naj eno datoteko z obema ocenama pri
vsakem študentu.
Namig
Kot pri vseh izpisih doslej, bomo le minimalno popravili metodo iz naloge 8.
[
rešitev]
Rešitve nalog
Naloga1.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| import java.io.*;
import java.util.*;
public class Naloga1 {
// Vaja 1
// Napravimo (statieno) metodo, ki kot argument dobi ime vhodne
// datoteke. Vse vrstice vhodne datoteke prebere v objekt razreda
// Vector, ki ga vrne kot vrednost. Elementi naj bodo v vektorju v
// enakem vrstnem redu kot so vrstice na vhodni datoteki.
public static Vector beri(String ime) throws IOException {
BufferedReader dat = new BufferedReader(new FileReader(ime));
Vector vv = new Vector();
while (dat.ready()) {
vv.add(dat.readLine());
}
dat.close();
return vv;
} // beri
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// izpišemo jih na standardni izhod
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
System.out.println(podatki);
} // main
} // class
|
Naloga2.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| import java.io.*;
import java.util.*;
public class Naloga2 {
// Vaja 2
// Napravimo (statično) metodo, ki kot argument dobi objekt razreda
// Vector in ime izhodne datoteke. Vse elemente vektorja izpiše na
// datoteko po enega v vrstico.
public static void pisi(Vector vv, String ime) throws IOException {
PrintWriter dat = new PrintWriter(new FileWriter(ime));
Iterator it = vv.iterator();
while (it.hasNext()) {
String x = (String)it.next();
dat.println(x);
}
dat.close();
} // pisi
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
pisi(podatki,args[1]);
} // main
} // class
|
Zahtevana metoda je metoda beriS
v spodnjem programu.
Komentar v metodi pojasnjuje delovanje.
Naloga3.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| import java.io.*;
import java.util.*;
public class Naloga3 {
// Vaja 3
// Napravimo (statično) metodo, ki kot argument dobi ime vhodne
// datoteke. Vse vrstice vhodne datoteke prebere v objekt razreda
// Vector, ki ga vrne kot vrednost. Vsako vrstico iz vhodne
// datoteke vstavimo v vektor tako, da bodo elementi v vektorju
// sortirani po abecedi.
public static Vector beriS(String ime) throws IOException {
// skelet metode je enak kot pri vaji 1
BufferedReader dat = new BufferedReader(new FileReader(ime));
Vector vv = new Vector();
while (dat.ready()) {
String ll = dat.readLine();
// Tu imamo prebrano vrstico ll
// Odlociti se moramo, kam v Vector vv jo bomo vstavili.
// Predpostavljamo, da so vsi dosedanji elementi vv že
// sortirani, kar pomeni, da moramo poiskati tisti kk, kjer
// je (prvič) ll <= vv.elementAt(kk).
// To naredimo tako, da s kk štejemo od 0 do vv.size()-1 in
// vsakokrat testiramo gornji pogoj; ko najdemo kk, ki ustreza
// gornjemu pogoju, vstavimo podatek ll v vektor vv in končamo
// zanko.
// Posebni (robni) problemi:
// 1. vektor je prazen: gornji pogoj ne more biti nikoli
// izpolnjen, saj ni nobenega elementa - v resnici se zanka
// ne bo nikoli izvedla in se bo "naravno" iztekla (brez
// break); nov podatek moramo dodati na konec vektorja
// (uporabiti vv.add(ll)).
// 2. podatek je večji od vseh dosedanjih: zanka se bo iztekla,
// ker gornji pogoj ni bil nikoli izpolnjen, nov element
// moramo dodati na konec vektorja (enako kot v prejšnjem
// primeru).
// 3. Podatek je manjši od vseh v vektorju: Zanka se bo ustavila
// v prvi ponovitvi in vse bo enako kot sicer.
// Gornja primera 1 in 2 torej združimo v enega: če se zanka
// naravno konča, napravimo vv.add(ll).
//
// Zgoraj je primer, kako je smiselno vedno pregledati robne
// pogoje - izrojene situacije. Skoraj vse programerske napake
// se pojavljajo v takih izjemnih situacijah.
//
// Ker potrebujemo indeks elementa za insertElementAt,
// uporabljamo kar elementAt in ne Iterator.
int kk = 0; // indeks elementa, ki ga pregledujemo
for (kk = 0; kk < vv.size(); kk++) {
if (ll.compareTo(vv.elementAt(kk)) <= 0) {
vv.insertElementAt(ll,kk); break; // to je pravo mesto
}
}
if (kk >=vv.size()) { // zanka se je naravno končala
vv.add(ll);
}
}
dat.close();
return vv;
} // beriS
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = beriS(args[0]);
Naloga2.pisi(podatki,args[1]);
} // main
} // class
|
Opomba:
Vector
nikakor ni najprimernejša podatkovna
struktura za
vstavljanje emphelementov.
Mi ga uporabljamo zato, ker primernejše (še) ne poznamo in ker smo se odločili, da bomo
delali vaje z razredom
Vector
.
Ne glede na podatkovno strukturo pa pristop sprotnega urejanja podatkov med branjem
večinoma nima smisla. Če bi bila celotna naloga le branje in urejen izpis (kot to
naredimo v testni metodi main), je pravi pristop branje, sortiranje in izpis.
Sprotno urejanje podatkov na načine kot so prikazani v teh rešitvah je prezahtevno
v primerjavi z dobrim sortiranjem. Tudi če bi zaradi kakega drugega razloga morali
imeti (med branjem) stalno urejene podatke, bi se morali tega lotiti drugače (npr.
z urejenim drevesom).
Naloga4.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| import java.io.*;
import java.util.*;
public class Naloga4 {
// Vaja 4
// Napravimo (statično) metodo, ki kot argument dobi objekt razreda
// Vector in ime izhodne datoteke. Vse elemente vektorja izpiše na
// datoteko. Elementi iz vektorja naj se izpišejo v abecednem
// vrstnem redu. Pri tem lahko vsebimo vektorja spremenimo (na
// koncu je lahko prazen).
public static void pisiS1(Vector vv, String ime) throws IOException {
// POZOR: ta metoda je grda, ker spreminja vv!!
PrintWriter dat = new PrintWriter(new FileWriter(ime));
while (vv.size() > 0) {
String min = (String)vv.elementAt(0); // doslej najmanjši
int indMin = 0; // indeks doslej najmanjšega
for (int ii = 1; ii < vv.size(); ii++) {
String x = (String)vv.elementAt(ii);
if (x.compareTo(min) < 0) {
min = x; indMin = ii;
}
}
dat.println(vv.remove(indMin));
}
dat.close();
} // pisiS1
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
pisiS1(podatki,args[1]);
} // main
} // class
|
Opomba: Vector
nikakor ni najprimernejša
podatkovna struktura za brisanje elementov. Mi ga
uporabljamo zato, ker primernejše (še) ne poznamo in ker smo
se odločili, da bomo delali vaje z razredom Vector
.
Naša metoda spreminja podatkovno strukturo, ki jo dobi kot
parameter. V splošnem to ni pametno obnašanje, ker uporabnik
metode na to enostavno pozabi in je zato tako obnašanje
pogost vir napak. Nalogo smo zastavili tako le zaradi
intuitivne preprostosti postopka. Naslednja naloga to grdo
obnašanje odpravi (metoda pisiS
).
Vsa sortiranja, ki smo jih napravili v našem programu so le
enostavni šolski postopki in niso za resno rabo. Za
resno delo moramo uporabiti postopke sortiranja, ki imajo
število operacij sorazmerno n * log(n) in ne
n2, kot je to v naših primerih (n
označuje število elementov tabele).
Naloga5.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| import java.io.*;
import java.util.*;
public class Naloga5 {
// Vaja 5
// Napravimo (statično) metodo, ki kot argument dobi objekt razreda
// Vector in ime izhodne datoteke. Vse elemente vektorja izpiše na
// datoteko. Elementi iz vektorja naj se izpišejo v abecednem
// vrstnem redu. Pri tem mora ostati vsebina vektorja nespremenjena.
public static void pisiS(Vector vv, String ime) throws IOException {
boolean[] zbrisan = new boolean[vv.size()]; // označuje zbrisane
PrintWriter dat = new PrintWriter(new FileWriter(ime));
int indMin; // indeks doslej najmanjšega; -1, če ga še ni
do { // računamo na to, da je tabela zbrisan nastavljena na false
String min = ""; // doslej najmanjši
indMin = -1;
for (int ii = 0; ii < vv.size(); ii++) {
if (!zbrisan[ii]) { // zbrisane preskočimo
String x = (String)vv.elementAt(ii);
if (indMin < 0 | x.compareTo(min) < 0) {
min = x; indMin = ii; // manjši od doslej najmanjšega
}
}
}
if (indMin >= 0) { // imamo minumum (sicer je vektor prazen)
dat.println(vv.elementAt(indMin));
zbrisan[indMin] = true;
}
} while (indMin >= 0);
dat.close();
} // pisiS
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
pisiS(podatki,args[1]);
} // main
} // class
|
Zahtevana metoda je metoda sort
v spodnjem programu.
Alternativna in malce boljša je metoda bubbleSort
.
Komentarji v programu pojasnjujejo delovanje.
Naloga6a.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| import java.io.*;
import java.util.*;
public class Naloga6a {
// Vaja 6
// Napravimo (statično) metodo, ki kot argument dobi objekt razreda
// Vector in vrne isti objekt, le da so elementi v njem sortirani po
// velikosti.
public static Vector sort(Vector vv) {
// Tole je primitiven postopek za urejanje (selection sort)
// ni za resno rabo, ker se izvaja s kvadratno kompleksnostjo
// tudi med kvadratnimi urejanji je ta postopek slab, ker teče
// kvadratno tudi na urejenih tabelah (bubble sort - mehurčno
// urejanje je prav tako enostavno, ampak boljše in celo uporabno
// za majhne tabele.
int ss = vv.size();
// sortiran del tabele bo rastel od začetka; s vsako ponovitvijo
// spodnje zanke bomo pridelali en element na pravem mestu
// spremenljivka zeSortiran kaže (na koncu zanke) na zadnji že
// sortiran element
for (int zeSortiran = 0; zeSortiran < ss-1; zeSortiran++) {
// poiščemo najmanjši element nesortiranega dela tabele
String min = (String)(vv.elementAt(zeSortiran));
int indMin = zeSortiran;
for (int ii = indMin+1; ii < ss; ii++) {
if (min.compareTo(vv.elementAt(ii)) > 0) {
min = (String)(vv.elementAt(ii)); indMin = ii;
}
}
// zamenjamo prvega nesortiranega in najmanšega
Object tt = vv.elementAt(zeSortiran);
vv.setElementAt(vv.elementAt(indMin),zeSortiran);
vv.setElementAt(tt,indMin);
}
return vv;
} // sort
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
sort(podatki);
Naloga2.pisi(podatki,args[1]);
} // main
} // class
|
Naloga6b.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| import java.io.*;
import java.util.*;
public class Naloga6b {
// Vaja 6
// Napravimo (statično) metodo, ki kot argument dobi objekt razreda
// Vector in vrne isti objekt, le da so elementi v njem sortirani po
// velikosti.
// DRUGA VERZIJA
public static Vector bubbleSort(Vector vv) {
// Tole je primitiven postopek za urejanje (bubble sort - mehurčno
// urejanje). Uporaben je za majhne tabele. Kompleksnost še vedno
// raste s kvadratom dolžine tabele.
int ss = vv.size();
// Ideja je naslednja (zaradi analogije s prejšnjim postopkom,
// bomo tudi ta postopek napisali tako, da že sortiran del na
// začetku, čeprav se ga tradicionalno napiše tako, da sortiran
// del tabele nastaja na koncu tabele):
//
// Prav tako kot prej, imamo tabelo razdeljeno na že sortiran
// in še nesortiran del.
// Prav tako bomo poiskali najmanjši element nesortiranega dela
// in ga dali na začetek, le da bomo to storili malce drugače.
// Od konca nesortiranega dela tabele bomo šli na začetek in
// vsakokrat primerjali dva sosednaj elementa. Če sta v napačnem
// vrstnem redu (večji pred manjšim), ju zamenjamo. Ker se
// najmanjši element nesortiranega dela vedno zamenja s sosedi,
// bo zagotovo prišel na vrh. Spotoma pa bomo nekaj reda napravili
// tudi med ostalimi pari.
// Ko smo prišli z najmanjšim elementom do vrha nesortiranega
// dela tabele, vemo, da so vsi elementi nad zadnjo menjavo
// zagotovo že sortirani, zato si lahko ustrezno označimo konec
// sortiranega dela rabele.
int zeSortiran = 0; // ni nobenega že sortiranega elementa
while (zeSortiran < ss) {
int zadnjaMenjava = zeSortiran;
// poiščemo najmanjši element nesortiranega dela tabele
for (int ii = ss-2; ii >= zeSortiran ; ii--) {
if (((String)(vv.elementAt(ii))).compareTo(vv.elementAt(ii+1)) > 0) {
Object tt = vv.elementAt(ii);
vv.setElementAt(vv.elementAt(ii+1),ii);
vv.setElementAt(tt,ii+1);
zadnjaMenjava = ii;
}
}
zeSortiran = zadnjaMenjava+1;
}
return vv;
} // bubbleSort
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga1.beri(args[0]);
bubbleSort(podatki);
// Collections.sort(podatki);
Naloga2.pisi(podatki,args[1]);
} // main
} // class
|
Opomba: Vsa sortiranja, ki smo jih napravili v okviru
teh vaj so le enostavni šolski postopki in niso za resno
rabo. Za resno delo moramo uporabiti postopke
sortiranja, ki imajo število operacij sorazmerno
n *log(n) in ne n2, kot je to
v naših primerih (n označuje število elementov
tabele).
Dobra metoda za sortiranje je npr.
sort
v razredu
java.util.Arrays
in posebna varianta
Collections.sort
v razredu
java.util.Collections
. Namesto klica naše metode
sort(vektor)
bi uporabili
Collections.sort(vektor); |
kot je zapisano v komentarju v
main
.
Razred Student definiramo takole:
Student.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| // Osnovni podatki o studentu: ime, priimek, ocena
public class Student implements Comparable {
// komponente
String ime;
String priimek;
int ocena;
// konstruira Studenta iz komponent
public Student(String ii, String pp, int oo) {
ime = ii;
priimek = pp;
ocena = oo;
} // Student
// konstruira Studenta iz niza, kjer so s TAB ločeni
// ime, priimek, ocena (v tem vrstnem redu)
public Student(String ss) {
String[] tt = ss.split("\\s"); // če bi hoteli imeti za delimiter
// le TAB, bi napisali \t; \\s ujame vse vrste presledkov
ime = tt.length > 0 ? tt[0] : "";
priimek = tt.length > 1 ? tt[1] : "";
try { ocena = Integer.parseInt(tt[2]); }
catch (Exception ee) { ocena = 0; }
} // Student
// podatke o Studentu vrnemo kot niz
public String toString() {
return ime + " " + priimek + " " + ocena;
} // toString
// običajna metoda compareTo,
// primerja priimek in nato ime (če sta priimka enaka)
public int compareTo(Object s2) {
int rv = 0;
Student ss = (Student)s2;
if ((rv = this.priimek.compareTo(ss.priimek)) != 0) return rv;
return this.ime.compareTo(ss.ime);
} // compareTo
// običajna metoda equals, primerja priimek in ime
public boolean equals(Object s2) {
return this.compareTo((Student)s2) == 0;
} // equals
} // class
|
Edina drobna komplikacija je v konstruktorju, ki konstruira
nov objekt iz niza. Uporablja metodo
split()
, ki
smo jo že večkrat srečali. Za vsako komponento posebej
preveri, ali je bila navedena v vhodni vrstici, da bo
program deloval tudi ob manjkajočih podatkih. Posebej se
potrudimo, da manjkajoča ali napačna ocena pomeni oceno 0.
V razredu smo pravilno deklarirali metodi equals
in
compareTo
zaradi zadnje smo lahko navedli, da
implementiramo vmesnik Comparable
. To nam bo
omogočilo uporabo splošnih metod za iskanje in sortiranje v
vektorju.
Programček iz naloge 1 lahko popravimo takole:
Naloga7.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| import java.io.*;
import java.util.*;
public class Naloga7 {
// Vaja 7
// Napravimo podobno metodo kot v prvi nalogi, le da vemo, da so na
// datoteki podatki sestavljeni iz imena in priimka tudenta ter
// ocene. Podatki na vhodni datoteki so med seboj ločeni z znaki
// TAB. Definirajmo razred Student, ki bo vseboval vse tri
// komponente. V razredu Student definirajmo tudi metodi toString
// in compareTo (tudente med seboj primerja po abecedi). Statična
// metoda naj vrne vektor objektov razreda Student.
// Razred Student je definiran na ločeni datoteki
public static Vector beriStudent(String ime) throws IOException {
BufferedReader dat = new BufferedReader(new FileReader(ime));
Vector vv = new Vector();
while (dat.ready()) {
{{vv.add(new Student(dat.readLine()));}}
}
dat.close();
return vv;
} // beriStudent
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// izpišemo jih na standardni izhod
public static void main(String[] args) throws IOException {
Vector podatki = beriStudent(args[0]);
System.out.println(podatki);
} // main
} // class
|
Edina spremenjena vrstica je označena.
V programu smo namesto split("\t")
zapisali kar split("\\s")
, kar
pomeni, da so ločila med podatki v vrsti lahko kakršnikoli presledki
("beli znaki", "whitespace"). Ker v posameznih podatkih (ime, priimek,
ocena) ne pričakujemo presledkov, je to povsem smiselno.
Testna datoteka ja npr. taka
Studenti1.dat |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| Alenka Žeks 5
Aljoša Balažek 1
Amir Cesar 8
Anže Deršek 2
Ana-Marija Đurić 5
Barbara Germovšek 4
Branka Hren 3
Damijana Javornik 8
Damjana Jerman 7
Gorazd Karba 4
Ivana Kirm 3
Katja Kožar 2
Maja Kojzek 5
Maja Komić 8
Marjana Leban 5
Matej Lesar 4
Melinda Levičar 6
Miran Medvešček 8
Natalija Mušič 4
Nina Pavlin 5
Nina Podbregar 9
Peter Sedej 2
Primož Skok 6
Renata Skok 1
Romana Strauss 6
Sebastjan Tomažič 6
Tadeja Valner 9
|
Opozorilo: datoteka vsebuje znake TAB, ki jih TextPad - če je tako
nastavljen - z veseljem spremeni v presledke.
Delovanje programa na gornjih testnih podatkih je tako:
> java Naloga7 Studenti1.dat
[Alenka Žeks 5, Aljoša Balažek 1, Amir Cesar 8, Anže Deršek 2, Ana-Ma
rija Đurić 5, Barbara Germovšek 4, Branka Hren 3, Damijana Javornik 8
, Damjana Jerman 7, Gorazd Karba 4, Ivana Kirm 3, Katja Kožar 2, Maja
Kojzek 5, Maja Komić 8, Marjana Leban 5, Matej Lesar 4, Melinda Levi
čar 6, Miran Medvešček 8, Natalija Mušič 4, Nina Pavlin 5, Nina Podbr
egar 9, Peter Sedej 2, Primož Skok 6, Renata Skok 1, Romana Strauss 6
, Sebastjan Tomažič 6, Tadeja Valner 9] |
Rešitev, ki uporablja metodo
toString
v razredu
Student
, je tule.
Naloga8.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| import java.io.*;
import java.util.*;
public class Naloga8 {
// Vaja 8
// Enaka nakoga kot naloga 2, le da so v vektorju objekti razreda
// Student.
public static void pisiStudent(Vector vv, String ime)
throws IOException {
PrintWriter dat = new PrintWriter(new FileWriter(ime));
Iterator it = vv.iterator();
while (it.hasNext()) {
{{Student x = (Student)it.next();}}
dat.println(x);
}
dat.close();
} // pisi
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
{{Vector podatki = Naloga7.beriStudent(args[0]);}}
{{pisiStudent(podatki,args[1]);}}
} // main
} // class
|
Metoda
pisiStudent
je skoraj enaka metodi
pisi
v nalogi 2. V resnici bi lahko v obeh primerih
deklarirali spremenljivko
x
namesto tipa
String
ali
Student
kar s tipom
Object
in v tem primeru bi bili obe metodi povsem
enaki.
Naloga9.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| import java.io.*;
import java.util.*;
public class Naloga9 {
// Vaja 9
// Enaka naloga kot naloga 3, le da so v vektorju objekti razreda
// Student.
public static Vector beriStudentS(String ime) throws IOException {
BufferedReader dat = new BufferedReader(new FileReader(ime));
Vector vv = new Vector();
while (dat.ready()) {
Student ll = new Student(dat.readLine()); /*M*/
int kk = 0; // indeks elementa, ki ga pregledujemo
for (kk = 0; kk < vv.size(); kk++) {
if (ll.compareTo((Student)vv.elementAt(kk)) <= 0) { /*M*/
vv.insertElementAt(ll,kk); break; // to je pravo mesto
}
}
if (kk >=vv.size()) { // zanka se je naravno končala
vv.add(ll);
}
}
dat.close();
return vv;
} // beriStudentS
// testni glavni program
// podatke preberemo iz datoteke, ki jo navedemo kot prvi argument
// in jih zapisemo na datoteko, ki jo navedemo kot drugi argument
public static void main(String[] args) throws IOException {
Vector podatki = beriStudentS(args[0]); /*M*/
Naloga8.pisiStudent(podatki,args[1]); /*M*/
} // main
} // class
|
Z upoštevanjem nasvetov v namigu k tej nalogi definiramo razred StudentR1 takole
StudentR1.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| // Dodatni podatki o studentu: ime, priimek, ocena
public class StudentR1 extends Student {
// komponente
int ocena2;
// konstruira StudentaR1 iz komponent
public StudentR1(String ii, String pp, int oo, int o2) {
super(ii,pp,oo);
ocena2 = o2;
} // StudentR1
// konstruira StudentaR1 iz niza, kjer so s TAB ločeni
// ime, priimek, ocena (v tem vrstnem redu)
public StudentR1(String ss) {
super(ss);
ocena2 = 0;
} // StudentR1
// podatke o StudentuR1 vrnemo kot niz
public String toString() {
return ime + " " + priimek + " " + ocena + " " + ocena2;
} // toString
// Spodnji dve metodi uporabljamo zato, ker vedno beremo le
// komponento ocena. Prvo verzijo uporabimo, ko smo nali tudenta,
// ki je e v tabeli. Drugo verzijo uporabimo, ko imamo tudenta,
// ki ga e ni v tabeli.
// oceno prepie iz student.ocena v this.ocena2
public void nastavi2(StudentR1 student) {
this.ocena2 = student.ocena;
} // nastavi2
// oceno prepie iz this.ocena v this.ocena2
public void nastavi2() {
this.ocena2 = this.ocena;
this.ocena = 0;
} // nastavi2
} // class
|
Obe verziji metod
nastavi2
sta pripravljeni zato, da bomo lahko prepisali
komponento
ocena
v
ocena2
.
Spodnja metoda
beriDva
prebere dve enako formatirani datoteki in vse
podatke vrne v vektorju.
Naloga10.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| import java.io.*;
import java.util.*;
public class Naloga10 {
// Vaja 10
// Na osnovi razreda Student napravimo razred StudentR1, ki bo
// vseboval vse komponente Studenta, poleg tega pa e oceno na
// drugem kolokviju. Napiimo statično metodo, ki bo dobila imeni
// dveh datotek (prve z rezultati prvega kolokvija in druge z
// rezultati drugega kolokvija).
// Metoda bo prebrala obe datoteki in vrnila vektor objektov
// razreda StudentR1. V vektorju se lahko vsak student pojavi le
// enkrat. Upotevajmo, da se lahko posamezni tudent pojavi le v
// eni datoteki, tedaj naj ima oceno iz drugega kolokvija enako 0.
public static Vector beriDve(String ime1, String ime2)
throws IOException {
// prvo datoteko beremo po starem (kot v nalogi 7)
// metode Naloga7.beriStudent() ne moremo direktno uporabljati,
// ker dela objekte razreda Student in ne StudentR1
BufferedReader dat = new BufferedReader(new FileReader(ime1));
Vector vv = new Vector();
while (dat.ready()) {
vv.add(new StudentR1(dat.readLine())); /*M*/
}
dat.close();
// preberemo e drugo datoteko
dat = new BufferedReader(new FileReader(ime2));
while (dat.ready()) {
// tudenta preberemo kar v nov objekt (zato, da konstruktor
// opravi vse delo)
StudentR1 ss = new StudentR1(dat.readLine()); /*M*/
int ind = vv.indexOf(ss);// indeks ss v vektorju (-1, če ga ni)
if (ind < 0) { // nov tudent
ss.nastavi2(); vv.add(ss);
}
else { // obstoječ tudent
((StudentR1)vv.elementAt(ind)).nastavi2(ss);
}
}
dat.close();
return vv;
} // beriDve
// testni glavni program
// podatke preberemo iz datotek, ki jo navedemo kot prvi in drugi argument
// izpi1emo jih na standardni izhod
public static void main(String[] args) throws IOException {
Vector podatki = beriDve(args[0], args[1]);
System.out.println(podatki);
} // main
} // class
|
Naloga11.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| import java.io.*;
import java.util.*;
public class Naloga11 {
// Vaja 11
// Metodo iz naloge 8 popravimo na razred StudentR1. Izpie naj eno
// datoteko z obema ocenama pri vsakem tudentu.
public static void pisiVse(Vector vv, String ime)
throws IOException {
PrintWriter dat = new PrintWriter(new FileWriter(ime));
Iterator it = vv.iterator();
while (it.hasNext()) {
StudentR1 x = (StudentR1)it.next(); /*M*/
dat.println(x);
}
dat.close();
} // pisiVse
// testni glavni program
// podatke preberemo iz datotek, ki jo navedemo kot prvi in drugi argument
// izpiemo jih na datoteko navedeno kot tretji argument
public static void main(String[] args) throws IOException {
Vector podatki = Naloga10.beriDve(args[0], args[1]); /*M*/
Collections.sort(podatki); // dodatno sortiranje (ni zahtevano) /*M*/
pisiVse(podatki,args[2]); /*M*/
} // main
} // class
|