4. Πίνακες Τελειώνοντας αυτό το κεφάλαιο θα μπορείτε: Να δηλώνετε και να δημιουργείτε πίνακες βασικών τύπων, κλάσεων ή άλλων πινάκων Να δηλώνετε την ανάγκη, και να μπορείτε να αρχικοποιήσετε τα στοιχεία ενός πίνακα Να καθορίζετε το πλήθος των στοιχείων σε οποιονδήποτε πίνακα Να γράφετε κώδικα για αντιγραφή πινάκων. 4.1 Δηλώσεις πινάκων Μπορείτε να δηλώσετε πίνακα οποιουδήποτε τύπου είτε βασικού είτε κλάσης: char s[]; Point p[]; Στη Java ένας πίνακας είναι μια κλάση και όπως και με τους άλλους τύπους κλάσης η δήλωση δε δημιουργεί και το αντικείμενο. Συνεπώς οι δηλώσεις αυτές δεν δημιουργούν πίνακες, απλά μεταβλητές αναφοράς που μπορούν να χρησιμοποιηθούν για αναφορά σε έναν πίνακα. Στην επόμενη σελίδα θα δούμε πώς δημιουργούμε και αρχικοποιούμε έναν πραγματικό πίνακα. Η μορφή που φαίνεται πιο πάνω, με τις αγκύλες μετά το όνομα της μεταβλητής, είναι η συνήθης για C και C++ και είναι η ίδια και στη Java. Η μορφή αυτή οδηγεί σε σύνθετες μορφές δηλώσεων και μπορεί να είναι δύσκολη στην ανάγνωση, συνεπώς η Java επιτρέπει μία εναλλακτική μορφή με τις αγκύλες στα αριστερά. char [] s; Point [] p; Η επιλογή σχετικά με το ποια από τις δύο μορφές θα χρησιμοποιείτε είναι δική σας, αρκεί να είστε συνεπείς στην απόφασή σας. 4.2 Δημιουργία πινάκων Μπορείτε να δημιουργήσετε πίνακες, όπως και όλα τα αντικείμενα, με χρήση της δεσμευμένης λέξης new, ως εξής: s = new char[20]; p = new Point[100]; Η πρώτη γραμμή δημιουργεί έναν πίνακα 20 τιμών τύπου char. Η δεύτερη γραμμή δημιουργεί ένα πίνακα με 100 μεταβλητές τύπου Point. Στην πράξη δε δημιουργεί 100 αντικείμενα τύπου Point. Αυτά θα πρέπει να δημιουργηθούν ξεχωριστά και ανεξάρτητα το καθένα, για παράδειγμα ως εξής: p[0] = new Point(); p[1] = new Point(); Σημείωση - Υπάρχουν πιο κομψοί τρόποι για αρχικοποίηση πίνακα που θα εισαχθούν στη συνέχεια.
4.3 Αρχικοποίηση πινάκων Όταν δημιουργείτε έναν πίνακα, κάθε στοιχείο του αρχικοποιείται. Στην περίπτωση του πίνακα s που αποτελείται από char κάθε τιμή αρχικοποιείται στο μηδενικό χαρακτήρα (\u0000, null). Στην περίπτωση του πίνακα p κάθε τιμή αρχικοποιείται σε null, δηλώνοντας ότι δεν αναφέρεται (ακόμα) σε ένα αντικείμενο Point. Μετά την ανάθεση p[0] = new Point() το πρώτο στοιχείο του πίνακα αναφέρεται σε πραγματικό αντικείμενο. Σημείωση Η αρχικοποίηση όλων των μεταβλητών, συμπεριλαμβανομένων των στοιχείων ενός πίνακα, είναι ουσιώδης για την ασφάλεια του συστήματος. Τίποτα δεν πρέπει να χρησιμοποιηθεί σε μη αρχικοποιημένη κατάσταση. Ο μεταγλωττιστής δεν μπορεί να ελέγξει μέσα από τη ροή τα στοιχεία ενός πίνακα. Η Java επιτρέπει μία συντόμευση για τη δημιουργία πινάκων με αρχικές τιμές. String names[] = { Georgianna, Jen, Simon, Tom }; Ο πιο πάνω κώδικας είναι ισοδύναμος με τον επόμενο String names[]; names = new String[4]; names[0] = Georgianna ; names[1] = Jen ; names[2] = Simon ; names[3] = Tom ; Η συντόμευση αυτή μπορεί να χρησιμοποιηθεί για οποιονδήποτε τύπο στοιχείου και απαιτεί αναφορά σε κάθε στοιχείο σε κάθε θέση στο μπλοκ αρχικοποίησης. Για παράδειγμα: Color palette[] = { Color.blue, Color.red, Color.white } 4.4 Πίνακες πολλαπλών διαστάσεων Η Java δεν παρέχει πίνακες πολλαπλών διαστάσεων ρητά, αλλά καθώς ένας πίνακας μπορεί να δηλωθεί ώστε να έχει οποιοδήποτε τύπο βάσης μπορούμε να δημιουργήσουμε πίνακες από πίνακες (από πίνακες...) int twodim [][] = new int [4][]; twodim[0] = new int[5]; twodim[1] = new int[5]; Το αντικείμενο που δημιουργείται από την κλήση στο new είναι απλά ένας πίνακας. Ο πίνακας περιέχει τέσσερα στοιχεία. Κάθε ένα από αυτά τα στοιχεία είναι μία null αναφορά
σε ένα στοιχείο τύπου array of int (πίνακες από ακεραίους). Συνεπώς κάθε ένα από τα τέσσερα στοιχεία θα πρέπει να δημιουργηθεί ξεχωριστά. Κάθε ένα από τα στοιχεία αυτά είναι ένα array of int. Σημείωση Αν και η μορφή της δήλωσης επιτρέπει οι αγκύλες να βρίσκονται στα αριστερά ή στα δεξιά του ονόματος της μεταβλητής η ευκολία αυτή δεν μεταφέρεται σε άλλες απόψεις του συντακτικού των πινάκων. Για παράδειγμα, το new int [] [4] δεν είναι επιτρεπτή. Εξαιτίας αυτού του διαχωρισμού είναι πιθανό να δημιουργηθούν μη ορθογώνιοι πίνακες από πίνακες. Δηλαδή τα στοιχεία του twodim μπορούν να αρχικοποιηθούν ως εξής: twodim[0] = new int[2]; twodim[1] = new int[4]; twodim[2] = new int[6]; twodim[3] = new int[8]; Καθώς αυτός ο τύπος αρχικοποίησης είναι διαφορετικός από αυτούς που έχουμε συνηθίσει και οι ορθογώνιοι πίνακες από πίνακες είναι η πιο συνηθισμένη μορφή, η Java παρέχει τη συντόμευση για δημιουργία δυσδιάστατων πινάκων. Για παράδειγμα η εντολή: int matrix[][] = new int [4][5]; μπορεί να χρησιμοποιηθεί για να δημιουργηθεί ένας πίνακας τεσσάρων πινάκων πέντε ακεραίων ο καθένας. 4.5 Όρια πινάκων Στη Java όλοι οι δείκτες των πινάκων ξεκινούν από το μηδέν. Το πλήθος των στοιχείων σε ένα πίνακα αποθηκεύεται ως μέρος του αντικειμένου πίνακα. Η τιμή αυτή χρησιμοποιείται για να πραγματοποιήσει ελέγχους ορίων σε όλες τις προσβάσεις κατά το χρόνο εκτέλεσης. Αν λάβει χώρα μία πρόσβαση εκτός ορίων τότε παρουσιάζεται μία εξαίρεση. Οι εξαιρέσεις είναι θέμα επομένου κεφαλαίου. Το μέγεθος ενός πίνακα μπορεί να καθοριστεί κατά το χρόνο εκτέλεσης χρησιμοποιώντας τη μεταβλητή μέλος length. Για παράδειγμα, για επαναληπτική διάσχιση ενός πίνακα μπορούμε να χρησιμοποιήσουμε τον επόμενο κώδικα int list[] = new int [10]; for (int i = 0; i < list.length; i++) // do the work Παρατηρήστε ότι το όριο του βρόχου καθορίζεται με σύγκριση με το list.length αντί με την άμεση τιμή 10. Αυτή η προσέγγιση είναι πιο εύρωστη για αντιμετώπιση της συντήρησης του προγράμματος. 4.6 Αντιγραφή πινάκων Από τη στιγμή που δημιουργείται ένας πίνακας δεν μπορεί να αλλάξει το μέγεθός του. Παρ όλα αυτά μπορείτε να χρησιμοποιήσετε την ίδια μεταβλητή αναφοράς για να αναφερθείτε σε ένα εντελώς διαφορετικό πίνακα. int elements[] = new int[6]; elements = new int[10];
Στην περίπτωση αυτή ο πρώτος πίνακας ουσιαστικά χάνεται, εκτός και αν υπάρχει κάποια άλλη αναφορά προς αυτόν. Είναι δυνατό να αντιγράψουμε έναν πίνακα με αποδεκτή απόδοση. Η Java παρέχει μία ειδική μέθοδο στην κλάση System. Η μέθοδος λέγεται arraycopy() και χρησιμοποιείται ως εξής: // original array int elements[] = new int {1, 2, 3, 4, 5, 6};... // new larger array int hold[] = new int {10, 9. 8, 7, 6, 5, 4, 3, 2, 1}; // copy all the elements array to the hold array // starting with the 0th index System.arraycopy(elements, 0, hold, 0, elements.length); 4.7 Εργασίες 4.7.1. Πρώτο επίπεδο: Βασική χρήση πινάκων 1. Δημιουργήστε μία κλάση BasicArray. Στη μέθοδο main() ορίστε δύο μεταβλητές με όνομα thisarray και thatarray. Θα πρέπει να είναι τύπου array of int. 2. Δημιουργήστε ένα ακόμα πίνακα με δέκα int τιμές στο εύρος από 1 έως 10. Αναθέστε την αναφορά αυτού του τρίτου πίνακα στο thisarray. 3. Χρησιμοποιήστε ένα βρόχο for() για να εκτυπώσετε όλες τις τιμές του thisarray. Πώς θα ελέγξετε το όριο του βρόχου; 4. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Πόσες τιμές εκτυπώθηκαν; Ποιες είναι οι τιμές αυτές; 5. Για κάθε στοιχείο του thisarray θέστε την τιμή του να είναι το παραγοντικό της τιμής του δείκτη. Εκτυπώστε τις τιμές του πίνακα. 6. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. 7. Αναθέστε την τιμή του thisarray στη μεταβλητή του thatarray. Εκτυπώστε όλα τα στοιχεία του thatarray. 8. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Πόσες τιμές παρουσιάζονται για το thatarray; Ποιες είναι αυτές οι τιμές και πώς προέκυψαν; 9. Τροποποιείστε ορισμένα από τα στοιχεία του thisarray. Εκτυπώστε τις τιμές του thatarray. 10. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Τι παρατηρείτε για το thatarray; 11. Δημιουργείστε έναν πίνακα από 20 ακεραίους. Αναθέστε την αναφορά του νέου πίνακα στη μεταβλητή thatarray. Εκτυπώστε τις τιμές και των δύο πινάκων. 12. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Πόσες τιμές παρουσιάζονται για κάθε πίνακα; Ποιες είναι αυτές οι τιμές;
13. Αντιγράψτε τις τιμές του thisarray στο thatarray. Ποια κλήση μεθόδου θα χρησιμοποιήσετε για να το επιτύχετε; Πώς θα περιορίσετε το πλήθος των μεθόδων που αντιγράφονται; Τι γίνεται στα στοιχεία 10 έως 19 του thatarray; 14. Εκτυπώστε τις τιμές του thatarray. 15. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Είχατε δίκιο σχετικά με τις τιμές που παρουσιάζονται; Αν όχι, καταλαβαίνετε τι έχει συμβεί και τι ήταν αυτό που παρανοήσατε; 16. Αλλάξτε ορισμένες από τις μεταβλητές στο thatarray. Εκτυπώστε αμφότερα τα thisarray και thatarray. 17. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Είναι οι τιμές αυτές που αναμένατε; 4.7.2. Δεύτερο επίπεδο: Πίνακες από πίνακες 1. Δημιουργείστε μία κλάση Array2D. Στη μέθοδο main() δηλώστε μία μεταβλητή που ονομάζεται twod, τύπου πίνακα από πίνακα από ακέραιους. Δηλώστε μία δεύτερη μεταβλητή και ονομάστε την oned, τύπου πίνακα από ακεραίους. 2. Δημιουργήστε ένα πίνακα με στοιχεία τύπου array of int. Ο πίνακας θα πρέπει να έχει τέσσερα στοιχεία. Αναθέστε την αναφορά στον πίνακα αυτό στο στοιχείο [0] της μεταβλητής twod. 3. Γράψτε δύο ένθετους βρόχους for για να εκτυπώσετε όλες τις τιμές του twod. Παρουσιάστε την έξοδο ως μήτρα. Η μέθοδος System.out.print θα σας φανεί χρήσιμη εδώ. 4. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Θα πρέπει να βρείτε ότι προκαλείται μία εξαίρεση δείκτη null καθώς τα στοιχεία [1] έως [3] του twod δεν είναι αρχικοποιημένα. 5. Δημιουργήστε επιπλέον πίνακες ακεραίων που να περιέχουν πέντε, έξι και επτά στοιχεία αντίστοιχα. Αναθέστε τις αναφορές τους στα στοιχεία [1], [2] και [3] του twod, αντίστοιχα. Βεβαιωθείτε ότι ο σχετικός κώδικας έχει εισαχθεί πριν τον ένθετο βρόχο for που περιγράψαμε στο 3. 6. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Αυτή τη φορά θα πρέπει να δείτε μία μη ορθογώνια παρουσίαση μηδενικών τιμών. 7. Αναθέστε σε κάθε στοιχείο των πινάκων του twod μία διακριτή μη μηδενική τιμή. 8. Αναθέστε, με τη σειρά, τις αναφορές στα τέσσερα στοιχεία του πίνακα twod στη μεταβλητή oned. Αμέσως μετά από κάθε ανάθεση με χρήση ενός βρόχου for παρουσιάστε τα περιεχόμενα του oned. Τοποθετείστε το σχετικό κώδικα πριν τον κώδικα που περιγράψαμε στο 3. 9. Μεταγλωττίστε και εκτελέστε το πρόγραμμα. Παρατηρήστε ότι οι ανεξάρτητοι πίνακες που παρουσιάζονται με εκτύπωση των τιμών του oned είναι οι ίδιοι με τα στοιχεία του πίνακα στο twod.
4.7.3. Τρίτο επίπεδο: παιχνίδι αναγραμματισμού 1. Δημιουργήστε μία εφαρμογή που έχει ένα πίνακα λέξεων (με οκτώ χαρακτήρες μήκος το πολύ) που θα αλλάζει τη σειρά των χαρακτήρων και θα παρουσιάζει τη νέα λέξη στο χρήστη. 2. Επιτρέψτε στο χρήστη να δει τον αναγραμματισμό και δώστε του 5 προσπάθειες για να βρει την αρχική λέξη.