Rabu, 14 Desember 2011

Array Dinamis (Java)



Panjang suatu array tidak bisa diubah setelah dibuat. Akan tetapi, sering kali jumlah data yang disimpan dalam array bisa berubah setiap saat. Misalnya dalam contoh berikut : Suatu array menyimpan baris teks dalam program pengolah kata. Atau suatu array yang menyimpan daftar komputer yang sedang mengakses halaman suatu website. Atau array yang berisi gambar yang ditambahkan user oleh program gambar. Di sini jelas, bahwa kita butuh sesuatu yang bisa menyimpan data di mana jumlahnya tidak tetap.

Array Setengah Penuh


Bayangkan suatu aplikasi di mana sejumlah item yang ingin kita simpan di dalam array akan berubah-ubah sepanjang program tersebut berjalan. Karena ukuran array tidak bisa diubah, maka variabel terpisah digunakan untuk menyimpan berapa banyak sisa tempat kosong yang masih bisa diisi di dalam array.

Bayangkan misalnya, suatu program yang membaca bilangan bulat positif dari user, kemudian menyimpannya untuk diproses kemudian. Program akan berhenti membaca input apabila input yang diterima bernilai 0 atau kurang dari nol. Bilangan input n tersebut kita simpa di dalam array bilangan dengan tipe int[]. Katakan banyaknya bilangan yang bisa disimpan tidak lebih dari 100 buah. Maka ukuran array bisa dibuat 100.

Akan tetapi program tersebut harus melacak berapa banyak bilangan yang sudah diambil dari user. Kita gunakan variabel terpisah bernama jmlBilangan. Setiap kali suatu bilangan disimpan di dalam array, nilai jmlBilangan akan bertambah satu.

Sebagai contoh sederhana, masi kita buat program yang mengambil bilangan yang diinput dari user, kemudian mencetak bilangan-bilangan tersebut dalam urutan terbalik. (Ini adalah contoh pengolahan yang membutuhkan array, karena semua bilangan harus disimpan pada suatu tempat. Banyak lagi contoh program misalnya, mencari jumlah atau rata-rata atau nilai maksimum beberapa bilangan, bisa dilakukan tanpa menyimpan bilangan tersebut satu per satu)
public class BalikBilanganInput {
 
    public static void main(String[] args) {
 
        int[] bilangan;  // Array untuk menyimpan nilai input dari user
        int jmlBilangan;    // Banyaknya bilangan yang sudah disimpan dalam array
        int bil;          // Bilangan yang diambil dari user
 
        bilangan = new int[100];   // Buat array dengan 100 bilangan int
        jmlBilangan = 0;                // Belum ada bilangan yang disimpan
 
        System.out.println("Masukkan bilangan bulat positif (paling banyak 100 bilangan)" + 
            ", masukkan nol untuk mengakhiri.");
 
        while (true) {
            System.out.print("? ");
            bil = KonsolIO.ambilInt();
            if (bil <= 0)
                break;
            bilangan[jmlBilangan] = bil;
            jmlBilangan++;
        }
 
        System.out.println("\nBilangan yang Anda masukkan dalam urutan terbalik adalah :\n");
 
        for (int i = jmlBilangan - 1; i >= 0; i--) {
            System.out.println( bilangan[i] );
        }
 
    } // akhir main();
 
}  // akhir kelas BalikBilanganInput 


Penting untuk diingat bahwa jmlBilangan memiliki dua peran. Yang pertama, adalah melacak banyaknya bilangan yang sudah dimasukkan di dalam array. Yang kedua adalah menentukan di mana indeks kosong berikutnya di dalam array. Misalnya, jika 4 bilangan sudah dimasukkan ke dalam array, maka bilangan-bilangan tersebut diisi pada array di posisi 0, 1, 2, dan 3. Maka posisi kosong berikutnya adalah posisi 4.

Ketika kita akan mencetak angka di dalam array, maka poisisi penuh berikutnya adalah di lokasi jmlBilangan - 1, sehingga perulangan for mencetak bilangan dari jmlBilangan - 1 hingga 0.

Mari kita lihat contoh lain yang lebih realistis. Misalkan kita ingin menulis program game, di mana pemain bisa masuk ke dalam game dan keluar dari game setiap saat. Sebagai programmer berorientasi objek yang baik, mungkin kita memiliki kelas bernama Pemainsebagai lambang pemain di dalam game. Daftar pemain yang sedang ada di dalam game, bisa disimpan di dalam arrayArrayPemain dengan tipe Pemain[].

Karena jumlah pemain bisa berubah-ubah maka kita bisa menggunakan variabel bantu, misalnya jumlahPemainAktif untuk mencatat banyaknya pemain yang sedang aktif berada di dalam game. Misalnya jumlah maksimum pemain di dalam game adalah 10 orang, maka kita bisa mendeklarasikan variabelnya sebagai :
Pemain[] ArrayPemain = new Pemain[10];  // Hingga 10 pemain.
int jumlahPemainAktif = 0;  // Di awal game, tidak ada pemain yang aktif
Setelah beberapa pemain masuk ke dalam game, variabel jumlahPemainAktif akan lebih dari 0, dan objek pemailn akan disimpan dalam array, misalnya ArrayPemain[0], ArrayPemain[1], ArrayPemain[2], dan seterusnya. Ingat bahwa pemainArrayPemain[jumlahPemainAktif] tidak ada. Prosedur untuk menambah pemain baru, secara sederhana :
// Tambah pemain di tempat kosong
ArrayPemain[jumlahPemainAktif] = pemainBaru; 
// And increment playerCt to count the new player.
jumlahPemainAktif++;  
Untuk menghapus seorang pemain mungkin sedikit lebih sulit karena kita tidak ingin meninggalkan lubang di tengah-tengah array. Misalnya kita ingin menghapus pemain pada indeks k pada ArrayPemain. Jika kita tidak peduli urutan pemainnya, maka salah satu caranya adalah memindahkan posisi pemain terakhir ke posisi pemain yang meninggalkan game, misalnya :
ArrayPemain[k] = ArrayPemain[jumlahPemainAktif - 1];
jumlahPemainAktif--;


Pemain yang sebelumnya ada di posisi k, tidak lagi ada di dalam array. Pemain yang sebelumnya ada di posisijumlahPemainAktif -1 sekarang ada di array sebanyak 2 kali. Akan tetapi sekarang ia berada di bagian yang valid, karena nilaijumlahPemainAktif kita kurangi dengan satu. Ingat bahwa setiap elemen di dalam array harus menyimpan satu nilai, akan tetapi satu-satunya nilai dari posisi 0 hingga jumlahPemainAktif - 1 akan tetap diproses seperti biasa.

Misalnya kita ingin menghapus pemain di posisi k, akan tetapi kita ingin agar urutan pemain tetap sama. Untuk melakukannya, semua pemain di posisi k+1 ke atas harus dipindahkan satu posisi ke bawah. Pemain k+ mengganti pemain k yang keluar dari game. Pemain k+2 mengganti pemain yang pindah sebelumnya, dan berikutnya. Kodenya bisa dibuat seperti
for (int i = k+1; i < jumlahPemainAktif; i++) {
    ArrayPemain[i-1] = ArrayPemain[i];
}
jumlahPemainAktif--;



Perlu ditekankan bahwa contoh Pemain di atas memiliki tipe dasar suatu kelas. Elemennya bisa bernilai null atau referensi ke suatu objek yang bertipe Pemain, Objek Pemain sendiri tidak disimpan di dalam array, hanya referensinya saja yang disimpan di sana. Karena aturan pemberian nilai pada Java, objek tersebut bisa saja berupa kelas turunan dari Pemain, sehingga mungkin juga array tersebut menyimpan beberapa jenis Pemain misalnya pemain komputer, pemain manusia, atau pemain lainnya, yang semuanya adalah kelas turunan dari Pemain.

Contoh lainnya, misalnya kelas BentukGeometri menggambarkan suatu bentuk geometri yang bisa digambar pada layar, dan ia memiliki kelas-kelas turunan yang merupakan bentuk-bentuk khusus, seperti garis, kotak, kotak bertepi bulat, oval, atau oval berisi warna, dan sebagainya. (BentukGeometri sendiri bisa berbentuk kelas abstrak, seperti didiskusikan sebelumnya). Kemudian array bertipe BentukGeometri[] bisa menyimpan referensi objek yang bertipe kelas turunan dari BentukGeometri. Misalnya, perhatikan contoh pernyataan berikut
BentukGeometri[] gambar = new BentukGeometri[100]; // Array untuk menyimpan 100 gambar.
gambar[0] = new Kotak();          // Letakkan beberapa objek di dalam array.
gambar[1] = new Garis();          // (Program betulan akan menggunakan beberapa
gambar[2] = new OvalBerwarna();    // parameter di sini
int jmlGambar = 3;  // Lacak jumlah objek di dalam array
bisa diilustrasikan sebagai berikut.
Array tersebut bisa digunakan dalam program gambar. Array bisa digunakan untuk menampung gambar-gambar yang akan ditampilkan. Jika BentukGeometri memiliki metode "void gambarUlang(Graphics g)" untuk menggambar pada grafik g, maka semua grafik dalam array bisa digambar dengan perulangan sederhana
for (int i = 0; i < jmlGambar; i++)
    gambar[i].gambarUlang(g);


Pernyataan "gambar[i].gambarUlang(g);" memanggil metode gambarUlang() yang dimiliki oleh masing-masing gambar pada indeks i di array tersebut. Setiap objek tahu bagaimana menggambar dirinya sendiri, sehingga perintah dalam perulangan tersebut sebetulnya melakukan tugas yang berbeda-beda tergantung pada objeknya. Ini adalah contoh dari polimorfisme dan pengolahan array.Array Dinamis

Dalam contoh-contoh di atas, ada batas tententu dalam jumlah elemennya, yaitu 100 int, 100 Pemain, dan 100 BentukGeometris. Karena ukuran array tidak bisa berubah, array tersebut hanya bisa menampung maksimum sebanyak elemen yang didefinisikan pada pembuatan array. Dalam banyak kasus, adanya batas maksimum tersebut tidak diinginkan. Kenapa harus bekerja dengan hanya 100 bilangan bulat saja, bukan 101?

Alternatif yang umum adalah membuat array yang sangat besar sehingga bisa digunakan untuk dalam kehidupan sehari-hari. Akan tetapi cara ini tidak baik, karena akan sangat banyak memori komputer yang terbuang karena tidak digunakan. Memori itu mungkin lebih baik digunakan untuk yang lain. Apalagi jika komputer yang akan digunakan tidak memiliki memori yang cukup untuk menjalankan program tersebut.

Tentu saja, cara yang lebih baik adalah apabila kita bisa mengubah ukuran array sesuka kita kapan saja. Ingat bahwa sebenarnya variabel array tidak menyimpan array yang sesungguhnya. Variabel ini hanya menyimpan referensi ke objek tersebut. Kita tidak bisa membuat array tersebut lebih besar, akan tetapi kita bisa membuat array baru yang lebih besar, kemudian mengubah isi variabel array tersebut ke array baru itu.

Tentunya kita harus mengkopi semua isi di array yang lama ke array baru. Array lama akan diambil oleh pemulung memori, karena ia tidak lagi digunakan.

Mari kita lihat kembali contoh game di atas, di mana ArrayPemain adalah array dengan tipe Pemain[ ] dan jumlahPemainAktif[/code] adalah jumlah pemain yang sudah digunakan array tersebut. Misalnya kita tidak ingin membuat limit banyaknya pemainnya yang bisa ikut main. Jika pemain baru masuk dan array tersebut sudah penuh, kita akan membuat array baru yang lebih besar.

Variabel ArrayPemain akan merujuk pada array baru. Ingat bahwa setelah ini dilakukan, ArrayPemain[0] akan menunjuk pada lokasi memori yang berbeda, akan tetapi nilai ArrayPemain[0] sama dengan sebelumnya. Berikut ini adalah kode untuk melakukan hal di atas:


// Tambah pemain baru, meskipun array sudah penuh
 
if (jumlahPemainAktif == ArrayPemain.length) {
    // Array sudah penuh. Buat array baru yang lebih besar,
    // kemudian kopi isi array lama ke array baru lalu ubah
    // ArrayPemain ke array baru.
    int ukuranBaru = 2 * ArrayPemain.length;  // Ukuran array baru
    Pemain[] temp = new Pemain[ukuranBaru];  // Array baru
    System.arraycopy(ArrayPemain, 0, temp, 0, ArrayPemain.length);
    ArrayPemain = temp;  // Ubah referensi ArrayPemain ke array baru.
}
 
// Di sini kita sudah tahu bahwa pasti ada tempat di array baru.
 
ArrayPemain[jumlahPemainAktif] = pemainBaru; // Tambah pemain baru...
jumlahPemainAktif++;  //    ... dan tambah satu jumlahPemainAktif nya
Jika kita akan melakukan hal ini terus menerus, akan lebih indah jika kita membuat kelas untuk menangani hal ini. Objek mirip array yang bisa berubah ukuran untuk mengakomodasi jumlah data yang bisa ia tampung disebut array dinamis. Array dinamis memiliki jenis operasi yang sama dengan array : mengisi nilai pada posisi tertentu dan mengambil nilai di posisi tertentu. Akan tetapi tidak ada batas maksimum dari jumlah array (hanya tergantung pada jumlah memori komputer yang tersedia). Dalam kelas array dinamis, metode put dan get akan diimplementasikan sebagai metode instansi.
Di sini misalnya, adalah kelas yang mengimplementasikan array dinamis int :
public class ArrayDinamisInt {
    private int[] data;  // Array untuk menyimpan data
    public DynamicArrayOfInt() {
        // Konstruktor.
        data = new int[1];  // Array akan bertambah besar jika diperlukan
    }
 
    public int get(int posisi) {
        // Ambil nilai dari posisi tertentu di dalam array.
        // Karena semua posisi di dalam array adalah nol, maka
        // jika posisi tertentu di luar data array, nilai 0 akan dikembalikan
        if (posisi >= data.length)
            return 0;
        else
            return data[posisi];
    }
 
    public void put(int posisi, int nilai) {
        // Simpan nilai ke posisi yang ditentukan di dalam array
        // Data array tersebut akan bertambah besar jika diperlukan
 
        if (posisi >= data.length) {
            // Posisi yang ditentukan berada di luar array data
            // Besarkan ukuran array 2x lipat. Atau jika ukurannya masih
            // terlalu kecil, buat ukurannya sebesar 2*posisi
 
            int ukuranBaru = 2 * data.length;
            if (posisi >= ukuranBaru)
                ukuranBaru = 2 * posisi;
            int[] dataBaru = new int[ukuranBaru];
            System.arraycopy(data, 0, dataBaru, 0, data.length);
            data = dataBaru;
 
            // Perintah berikut hanya untuk demonstrasi
            System.out.println("Ukuran array dinamis diperbesar menjadi "
                + ukuranBaru);
        }
        data[posisi] = nilai;
    }
 
} // akhir kelas ArrayDinamisInt

Data pada objek ArrayDinamisInt disimpan dalam array biasa, akan tetapi arraynya akan dibuang dan diganti dengan array baru yang lebih besar apabila diperlukan. Jika bilangan adalah variable bertipe ArrayDinamisInt, maka perintahbilangan.put(pos,nilai) akan menyimpan bilangan pada posisi pos di array dinamis tersebut. Fungsi bilangan.get(pos)mengambil nilai yang disimpan pada posisi pos.

Pada contoh pertama, kita menggunakan array untuk menyimpan bilangan bulat positif yang dimasukkan oleh user. Kita bisa menulis ulang program tersebut dengan menggunakan ArrayDinamisInt. Referensi ke bilangan[i] diganti denganbilangan.get[i]. Perintah "bilangan[jmlBilangan] = bil;" kita ganti dengan "bilangan.put(jmlBilangan,bil);". Berikut ini adalah programnya:
public class BalikBilanganInput {
 
    public static void main(String[] args) {
 
        ArrayDinamisInt bilangan;  // Array untuk menyimpan nilai input dari user
        int jmlBilangan;    // Banyaknya bilangan yang sudah disimpan dalam array
        int bil;          // Bilangan yang diambil dari user
 
        bilangan = new ArrayDinamisInt();
        jmlBilangan = 0;                // Belum ada bilangan yang disimpan
 
        System.out.println("Masukkan bilangan bulat positif, masukkan nol untuk mengakhiri.");
 
        while (true) {
            System.out.print("? ");
            bil = KonsolIO.ambilInt();
            if (bil <= 0)
                break;
            bilangan.put(jmlBilangan,bil);
            jmlBilangan++;
        }
 
        System.out.println("\nBilangan yang Anda masukkan dalam urutan terbalik adalah :\n");
 
        for (int i = jmlBilangan - 1; i >= 0; i--) {
            System.out.println( bilangan.get(i) );
        }
 
    } // akhir main();
 
}  // akhir kelas BalikBilanganInput 

1 komentar: