Sunday, December 25, 2016

Bab 8. Visual C# Untuk Programer


Koleksi






8.1 Pengantar

.NET Framework menyediakan tiga namespace yang didedikasikan untuk koleksi. Namespace System.Collections memuat koleksi-koleksi yang menyimpan referensi-referensi ke object-object. Anda perlu mencantumkan namespace ini karena ia kaya akan akan kode yang dipakai pada industri perangkat lunak dewasa ini. Hampir semua aplikasi mutakhir menggunakan koleksi pada namespace System.Collections.Generic, yang memuat kelas-kelas generik seperti List<T> dan Dictionary<K,V>.

Namespace System.Collections.Specialized memuat beberapa koleksi yang mendukung tipe-tipe spesifik, seperti string dan bit. Anda dapat belajar tentang namespace ini di halaman

msdn.microsoft.com/en-us/library/system.collections.specialized.aspx.

Koleksi-koleksi pada namespace ini menyediakan komponen-komponen yang dapat didaur-ulang terstandarisasi, sehingga Anda tidak perlu menuliskan kelas-kelas koleksi sendiri.

8.2 Sekilas Tentang Koleksi
Semua kelas koleksi pada .NET Framework mengimplementasikan beberapa kombinasi dari antarmuka-antarmuka koleksi. Setiap antarmuka ini mendeklarasikan operasi-operasi yang dilakukan secara generik pada pelbagai tipe koleksi. Gambar 8.1 mencantumkan beberapa antarmuka pada koleksi .NET Framework. Semua antarmuka pada Gambar 8.1 dideklarasikan pada namespace System.Collections dan memiliki ekivalensi generik pada namespace System.Collections.Generic. Implementasi dari tiap antarmukan ini disediakan pada .NET Framework. Programer juga bisa menyediakan implementasi spesifik untuk memenuhi kebutuhannya.

Antarmuka
Penjelasan
ICollection
Antarmuka yang diwarisi oleh antarmuka IList dan IDictionary. Memuat sebuah properti Count untuk menentukan ukuran koleksi dan sebuah metode CopyTo untuk menyalin isi sebuah koleksi ke array tradisional.

IList
Sebuah koleksi terurut yang dapat dimanipulasi seperti array. Menyediakan sebuah indekser untuk mengakses elemen-elemen dengan indeks int. Juga memiliki metode-metode untuk melakukan pencarian dan pemodifikasian sebuah koleksi, termasuk Add, Remove, Contains, dan IndexOf.

IDictionary
Sebuah koleksi yang memuat nilai-nilai, yang diindeks oleh objek “key”. Menyediakan sebuah indekser untuk mengakses elemen-elemen dengan sebuah indeks objek dan metode-metode untuk memodifikasi koleksi (misalnya, Add, Remove). Properti Keys pada antarmuka ini memuat objek-objek yang dipakai sebagai indeks, dan properti Values memuat semua objek yang disimpan.

IEnumerable
Sebuah objek yang dapat dienumerasi. Antarmuka ini hanya memuat satu metode, GetEnumerator, yang menghasilkan sebuah objek IEnumerator. Antarmuka ICollections mewarisi IEnumerable, jadi semua kelas koleksi mengimplementasikan IEnumerable secara langsung maupun tak-langsung.


Gambar 8.1 Beberapa antarmuka koleksi pada namespace System.Collections

Pada versi C# sebelumnya, .NET Framework hanya menyediakan kelas-kelas koleksi pada namespace System.Collections dan System.Collections.Specialized. Kelas-kelas ini menyimpan dan memanipulasi referensi-referensi object-object. Anda dapat menyimpan sembarang object di dalam sebuah koleksi. Aspek yang menyulitkan pada penyimpanan referensi-referensi object-object terjadi ketika membacanya dari koleksi. Aplikasi normalnya memproses tipe-tipe spesifik dari objek. Hasilnya, referensi-referensi object-object yang didapatkan dari sebuah koleksi perlu didowncast menjadi tipe yang sesuai agar aplikasi dapat memproses objek-objek secara tepat.

.NET Framework juga memuat namespace System.Collections.Generic, yang menggunakan kapabilitas generik yang telah dikenalkan sebelumnya. Banyak dari kelas-kelas pada namespace tersebut merupakan kelas-kelas ekivalen dari namespace System.Collections.

Pada bab ini akan didemonstrasikan kelas-kelas koleksi Array, ArrayList, Stack, Hashtable, SortedDictionary generik, dan LinkedList generik. Namespace System.Collections menyediakan beberapa struktur data lain, termasuk BitArray (sebuah koleksi yang memuat nilai-nilai true/false), Queue dan SortedList (sebuah koleksi dengan pasangan-pasangan kunci/nilai yang diurutkan menggunakan kunci dan dapat diakses dengan kunci atau dengan indeks). Gambar 8.2 mencantumkan beberapa kelas koleksi yang akan didiskusikan. Di sini juga akan didiskusikan mengenai antarmuka IEnumerator. Kelas-kelas koleksi dapat menciptakan enumerator yang memampukan programer untuk menjelajah koleksi-koleksi.

Kelas
Mengimplementasikan
Penjelasan
namespace System:
Array
IList
Kelas basis untuk semua array konvensional.

namespace System.Collections:
ArrayList
IList

Menyerupai array konvensional, tetapi ia dapat mengekspansi atau menyusut jika diperlukan untuk mengakomodasi jumlah elemen.
BitArray
ICollection
Sebuah array efisien dengan elemen-elemen bertipe bool.
Hashtable
IDictionary
Sebuah koleksi yang memuat pasangan-pasangan kunci/nilai tak-terurut yang dapat diakses dengan kunci.
Queue
ICollection
Sebuah koleksi dalam tatanan FIFO (first-in, first-out).
SortedList
IDictionary
Sebuah koleksi yang memuat pasangan-pasangan kunci/nilai yang dapat diurutkan dengan kunci dan yang dapat diakses dengan kunci atau dengan indeks.
Stack
ICollection
Sebuah koleksi dalam tatanan LIFO (last-in, first-out).
namespace System.Collections.Generic:
Dictionary< K, V >
IDictionary< K, V >
Sebuah koleksi yang memuat pasangan-pasangan kunci/nilai tak-terurut yang dapat diakses dengan kunci.
LinkedList< T >
ICollection< T >
Sebuah senarai berantai ganda.
List< T >
IList< T >
Sebuah ArrayList generik.
Queue< T >
ICollection< T >
Sebuah Queue generik.
SortedDictionary<
K, V >
IDictionary< K, V >
Sebuah Dictionary yang mengurutkan data berdasarkan kunci-kunci pada suatu pohon biner.
SortedList< K, V >
IDictionary< K, V >
Sebuah SortedList generik.
Stack< T >
ICollection< T >
Sebuah Stack generik.

Gambar 8.1 Beberapa antarmuka koleksi pada namespace System.Collections

8.3 Kelas Array dan Enumerator
Semua array secara implisit mewarisi kelas basis abstrak Array (namespace System); kelas ini mendefinisikan properti Length, yang menetapkan jumlah elemen pada array. Selain itu, kelas Array menyediakan metode-metode static yang menyediakan algoritma-algoritma untuk pemrosesan array. Umumnya, kelas Array mengoverload metode-metode tersebut. Misalnya, metode Reverse dapat membalikkan urutan dari elemen-elemen array atau dapat membalikkan elemen-elemen pada rentang elemen tertentu pada array.

Gambar 8.3 mendemonstrasikan beberapa metode static dari kelas Array.

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
71
72
73
74
75
// Gambar 8.3: MenggunakanArray.cs
// Metode-metode static pada kelas Array untuk memanipulasi array.
using System;
using System.Collections;

// mendemonstrasikan algoritma-algoritma dari kelas Array
public class MenggunakanArray
{
    private static int[] nilai2Int = { 1, 2, 3, 4, 5, 6 };
    private static double[] nilai2Double = { 8.4, 9.3, 0.2, 7.9, 3.4 };
    private static int[] nilai2SalinInt;

    // metode Main mendemonstrasikan metode-metode dari kelas Array
    public static void Main( string[] args )
    {
        nilai2SalinInt = new int[ nilai2Int.Length ]; // default menjadi nol

        Console.WriteLine( "Nilai-nilai awal array:\n" );
        TampilArray(); // menampilkan isi awal dari array

        // mengurutkan nilai2Double
        Array.Sort( nilai2Double );

        // menyalin nilai2Int ke nilai2SalinInt
        Array.Copy( nilai2Int, nilai2SalinInt, nilai2Int.Length );

        Console.WriteLine( "\nNilai-nilai array setelah Sort dan Copy:\n" );
        TampilArray(); // menampilkan isi array
        Console.WriteLine();

        // mencari 5 di dalam nilai2Int
        int hasil = Array.BinarySearch( nilai2Int, 5 );
        if ( hasil >= 0 )
            Console.WriteLine( "5 ditemukan pada elemen {0} di dalam nilai2Int",
                hasil );
        else
            Console.WriteLine( "5 tidak ditemukan di dalam nilai2Int" );

        // mencari 8763 di dalam nilai2Int
        hasil = Array.BinarySearch( nilai2Int, 8763 );
        if ( hasil >= 0 )
            Console.WriteLine( "8763 ditemukan pada elemen {0} di dalam nilai2Int",
                hasil );
        else
            Console.WriteLine("8763 tidak ditemukan di dalam nilai2Int");
    } // akhir Main

    // menampilkan isi array dengan enumerator
    private static void TampilArray()
    {
        Console.Write( "nilai2Double: " );

        // beriterasi menjelajahi array double dengan sebuah
IEnumerator enumerator = nilai2Double.GetEnumerator();

while ( enumerator.MoveNext() )
    Console.Write( enumerator.Current + " " );

        Console.Write( "\nnilai2Int: " );

        // beriterasi menjelajahi array int dengan sebuah
enumerator = nilai2Int.GetEnumerator();

while ( enumerator.MoveNext() )
    Console.Write( enumerator.Current + " " );

        Console.Write( "\nnilai2SalinInt: " );

        // beriterasi menjelajahi array int kedua dengan statemen foreach
foreach ( var elemen in nilai2SalinInt )
    Console.Write( elemen + " " );

        Console.WriteLine();
    } // akhir metode TampilArray
} // akhir kelas MenggunakanArray

Nilai-nilai awal array:

nilai2Double: 8.4 9.3 0.2 7.9 3.4
nilai2Int: 1 2 3 4 5 6
nilai2SalinInt: 0 0 0 0 0 0

Nilai-nilai array setelah Sort dan Copy:

nilai2Double: 0.2 3.4 7.9 8.4 9.3
nilai2Int: 1 2 3 4 5 6
nilai2SalinInt: 1 2 3 4 5 6

5 ditemukan pada elemen 4 di dalam nilai2Int
8763 tidak ditemukan di dalam nilai2Int

Direktif using pada baris 3-4 mencantumkan namespace System (untuk kelas Array dan Console) dan namespace System.Collections (untuk antarmuka IEnumerator).

Kelas uji ini mendeklarasikan tiga variabel static (baris 9-11). Dua baris pertama menginisialisasi nilai2Int dan nilai2Double dengan array int dan array double. Variabel static nilai2SalinInt dimaksudkan untuk mendemonstrasikan metode Copy dari kelas Array, jadi ia diberikan nilai default null.

Baris 16 menginisialisasi nilai2SalinInt dengan sebuah array int yang memiliki panjang sama dengan array nilai2Int. Baris 19 memanggil metode TampilArray (baris 49-74) untuk menampilkan isi awal dari ketiga array. Anda dapat melihat dari keluaran bahwa setiap elemen array nilai2SalinInt diinisialisasi dengan nilai default 0.

Baris 22 menggunakan metode Sort dari kelas Array untuk mengurutkan array nilai2Double. Ketika metode ini selesai dieksekusi, array akan memiliki elemen-elemen yang terurut dalam tatanan menaik. Elemen-elemen pada array harus mengimplementasikan antarmuka IComparable.

Baris 25 menggunakan metode Copy dari kelas Array untuk menyalin elemen-elemen dari array nilai2Int ke array nilai2SalinInt. Argumen pertama adalah array yang akan disalin (nilai2Int), argumen kedua merupakan array destinasi (nilai2SalinInt), dan argumen ketiga adalah sebuah int yang merepresentasikan jumlah elemen yang akan disalin (pada kasus ini, nilai2Int.Length menetapkan semua elemen).

Baris 32 dan 40 memanggil metode BinarySearch dari kelas Array untuk melakukan pencarian biner atas array nilai2Int. Metode BinarySearch menerima array terurut dan kunci pencarian sebagai argumen. Metode ini menghasilkan indeks pada array dimana ia menemukan kunci yang dicari (atau menghasilkan nilai balik negatif jika kunci tidak ditemukan). BinarySearch mengasumsikan bahwa ia menerima array terurut. Kinerja metode ini untuk array tak-terurut tidak dapat didefinisikan dan tak-terduga.

Metode TampilArray (baris 49-74) menggunakan metode-metode dari kelas Array untuk menjelajah tiap array. Metode GetEnumerator (baris 54) mendapatkan sebuah enumerator untuk array nilai2Double. Ingat bahwa kelas Array mengimplementasikan antarmuka IEnumerable. Semua array mewarisi secara implisit dari kelas Array, jadi kedua tipe array int[ ] dan double[ ] mengimplementasikan metode GetEnumerator dari antarmuka IEnumerable, yang menghasilkan sebuah enumerator yang dapat beriterasi menjelajah koleksi. Antarmuka IEnumerable (yang diimplementasikan oleh semua enumerator) mendefinisikan metode MoveNext dan Reset dan properti Current. Metode MoveNext memajukan enumerator ke elemen depan pada koleksi. Pemanggilan pertama terhadap MoveNext memposisikan enumerator pada elemen pertama dari koleksi. Metode MoveNext menghasilkan true jika terdapat sedikitnya satu elemen lagi di dalam koleksi; sebaliknya, metode ini menghasilkan false. Metode Reset memposisikan enumerator ke posisi sebelum elemen pertama pada koleksi. Metode MoveNext dan Reset melemparkan sebuah eksepsi InvalidOperationException jika isi koleksi dimodifikasi setelah enumerator diciptakan. Properti Current menghasilkan objek pada lokasi sekarang (terkini) di dalam koleksi.

Ketika sebuah enumerator dihasilkan oleh metode GetEnumerator pada baris 54, ia awalnya diposisikan ke posisi sebelum elemen pertama pada array nilai2Double. Kemudian ketika baris 56 memanggil MoveNext pada iterasi pertama di dalam loop while, enumerator maju ke elemen pertama pada array nilai2Double. Statemen while pada baris 56-57 beriterasi menjelajahi tiap elemen sampai enumerator melewati akhir dari array nilai2Double dan MoveNext menghasilkan false. Pada tiap iterasi, Anda menggunakan properti Current pada enumerator untuk membaca dan menampilkan elemen array terkini. Baris 62-65 beriterasi menjelajah array nilai2Int.

Perhatikan bahwa TampilArray dipanggil dua kali (baris 19 dan 28), jadi GetEnumerator dipanggil dua kali pada nilai2Double. Metode GetEnumerator (baris 54 dan 62) selalu menghasilkan sebuah enumerator yang diposisikan pada posisi sebelum elemen pertama. Perhatikan pula bahwa properti Current adalah read-only. Enumerator tidak dapat dipakai untuk memodifikasi isi koleksi, karena ia hanya dipakai untuk membaca elemen-elemen koleksi.

Baris 70-71 menggunakan sebuah statemen foreach untuk beriterasi menjelajah elemen-elemen koleksi seperti sebuah enumerator. Pada dasarnya, statemen foreach berperilaku seperti enumerator. Keduanya (statemen foreach dan enumerator) menjelajahi elemen-elemen sebuah array satu demi satu secara berurutan). Keduanya tidak membolehkan Anda untuk memodifikasi elemen-elemen selama iterasi. Statemen foreach secara implisit mendapatkan sebuah enumerator melalui metode GetEnumerator dan menggunakan metode MoveNext dan properti Current dari enumerator itu menjelajah koleksi. Karena alasan ini, Anda dapat menggunakan statemen foreach untuk menjelajah sembarang koleksi yang mengimplementasikan antarmuka IEnumerable, bukan hanya untuk array. Fungsionalitas ini akan didemonstrasikan pada bagian selanjutnya ketika kelas ArrayList didiskusikan.

Metode-metode static lain dari kelas Array mencakup Clear (menetapkan suatu rentang elemen menjadi 0, false, atau null), CreateInstance (menciptakan sebuah array baru dengan tipe tertentu), IndexOf (mencari lokasi kemunculan pertama dari sebuah objek pada suatu array atau pada potongan array), LastIndexOf (mencari lokasi kemunculan terakhir dari sebuah objek pada suatu array atau pada potongan array), dan Reverse (membalikkan urutan isi array atau potongan array).

8.4 Koleksi Tak-Generik
Namespace System.Collections pada Pustaka Kelas .NET Framework merupakan sumber primer bagi koleksi-koleksi tak-generik. Kelas-kelas ini menyediakan implementasi standar bagi banyak struktur data dengan koleksi-koleksi yang menyimpan referensi-referensi bertipe object. Pada bagian ini, akan didemonstrasikan kelas ArrayList, Stack, dan Hashtable.

8.4.1 Kelas ArrayList
Pada kebanyakan bahasa pemrograman, array konvensional memiliki ukuran tetap. Array tidak dapat diubah secara dinamis untuk mengikuti kebutuhan memori pada saat eksekusi. Pada beberapa aplikasi, keterbatasan ukuran-tetap ini menyebabkan masalah bagi para programer. Mereka harus memilih antara penggunaan array berukuran-tetap yang cukup besar untuk menyimpan jumlah elemen maksimum yang dibutuhkan aplikasi atau menggunakan struktur data dinamis yang dapat mengembang dan menyusut secara dinamis mengikuti kebutuhan pada saat eksekusi aplikasi.

Koleksi ArrayList pada .NET Framework menyerupai fungsionalitas dari array konvensional dan menyediakan pengaturan-ukuran koleksi secara dinamis pada metode-metode kelas. Kelas ArrayList memuat sejumlah tertentu elemen yang kurang dari atau sama dengan kapasitasnya. Aplikasi dapat memanipulasi kapasitas ArrayList dengan properti Capacity.


Kelas ArrayList menyimpan referensi-referensi ke object-object. Semua kelas mewarisi kelas object, jadi ArrayList dapat memuat objek-objek dengan tipe sembarang. Gambar 8.4 menyajikan beberapa metode dan properti dari kelas ArrayList.

Metode
Penjelasan
Add
Menambah sebuah objek pada ArrayList dan menghasilkan sebuah int yang menetapkan indeks dimana objek itu ditambahkan.

Capacity
Properti yang mendapatkan dan menetapkan jumlah elemen yang saat ini sedang disediakan pada ArrayList.

Clear
Menghapus semua elemen dari ArrayList.

Contains
Menghasilkan true jika objek-objek tertentu berada di dalam ArrayList; sebaliknya, menghasilkan false.

Count
Properti read-only yang mendapatkan jumlah elemen yang disimpan di dalam ArrayList.

IndexOf
Menghasilkan indeks kemunculan pertama dari objek tertentu pada ArrayList.

Insert
Menyisipkan sebuah objek pada indeks tertentu.

Remove
Menghapus kemunculan pertama dari objek tertentu.

RemoveAt
Menghapus sebuah objek pada indeks tertentu.

RemoveRange
Menghapus sejumlah elemen tertentu diawali dari indeks tertentu pada ArrayList.

Sort
Mengurutkan ArrayList.
TrimToSize
Menetapkan properti Capacity dari ArrayList menjadi sejumlah elemen yang sekarang sedang dimuat di dalam ArrayList (Count).


Gambar 8.4 Beberapa metode dan properti dari kelas ArrayList

Gambar 8.5 mendemonstrasikan kelas ArrayList dan beberapa metodenya. Kelas ArrayList berada di dalam namespace System.Collections (baris 4). Baris 8-11 mendeklarasikan dua array bertipe string (warna2 dan hapusWarna) yang akan dipakai untuk mengisi dua objek ArrayList. Ingat bahwa konstanta harus diinisialisasi pada waktu kompilasi, tetapi variabel readonly dapat diinisialisasi pada waktu eksekusi. Array merupakan objek yang diciptakan pada waktu eksekusi, jadi Anda mendeklarasikan warna2 dan hapusWarna dengan readonly, bukan const.

Ketika aplikasi memulai eksekusi, Anda menciptakan sebuah ArrayList dengan kapasitas awal satu elemen dan menyimpannya pada variabel list (baris 16). Statemen foreach pada baris 19-20 menambahkan lima elemen dari array warna2 pada list melalui metode Add pada kelas ArrayList, sehingga list mengembang untuk mengakomodasi elemen-elemen baru tersebut. Baris 24 menggunakan konstruktor teroverload ArrayList untuk menciptakan sebuah ArrayList baru yang diinisialisasi dengan isi dari array hapusWarna, kemudian menugaskannya kepada variabel hapusList. Konstruktor ini dapat menginisialisasi isi dari sebuah ArrayList dengan elemen-elemen dari sembarang ICollection yang dilewatkan kepadanya. Banyak kelas koleksi memiliki konstruktor semacam itu. Perhatikan bahwa konstruktor pada baris 24 melakukan tugas dari baris 19-20.

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
// Gambar 8.5: UjiArrayList.cs
// Menggunakan kelasArrayList.
using System;
using System.Collections;

public class UjiArrayList
{
    private static readonly string[] warna2 =
        { "MAGENTA", "MERAH", "PUTIH", "BIRU", "COKLAT" };
    private static readonly string[] hapusWarna =
        { "MERAH", "PUTIH", "BIRU" };

    // menciptakan ArrayList, menambahkan warna2 padanya dan memanipulasinya
    public static void Main( string[] args )
    {
        ArrayList list = new ArrayList( 1 ); // kapasitas awal 1

        // menambahkan elemen-elemen dari warna2 pada ArrayList list
        foreach ( var warna in warna2 )
            list.Add( warna ); // menambahkan warna pada ArrayList list

        // menambahkan elemen-elemen di dalam hapusWarna pada
        // ArrayList hapusList dengan konstruktor ArrayList
        ArrayList hapusList = new ArrayList( hapusWarna );

        Console.WriteLine( "ArrayList: " );
        TampilInformasi( list ); // menampilkan list

        // menghapus dari ArrayList list warna-warna di dalam hapusList
        HapusWarna( list, hapusList );

        Console.WriteLine( "\nArrayList setelah pemanggilan HapusWarna: " );
        TampilInformasi( list ); // menampilkan isi list
    } // akhir Main

    // menampilkan informasi tentang isi ArrayList
    private static void TampilInformasi( ArrayList arrayList )
    {
        // menjelajah array list dengan statemen foreach
foreach ( var elemen in arrayList )
    Console.Write( "{0} ", elemen ); // memanggil ToString

        // menampilkan isi dan kapasitas
        Console.WriteLine("\nUkuran = {0}; Kapasitas = {1}",
            arrayList.Count, arrayList.Capacity);

        int indeks = arrayList.IndexOf( "BIRU" );

        if ( indeks != -1 )
            Console.WriteLine( "ArrayList memuat BIRU pada indeks {0}.",
                indeks );
        else
            Console.WriteLine( "ArrayList tidak memuat BIRU." );
    } // akhir metode TampilInformasi

// menghapus warna-warna pada listKedua dari listPertama
private static void HapusWarna( ArrayList listPertama,
    ArrayList listKedua )
{
    // menjelajah ArrayList kedua seperti array biasa
    for ( int hitung = 0; hitung < listKedua.Count; hitung++ )
        listPertama.Remove( listKedua[ hitung ] );
} // akhir metode HapusWarna
} // akhir kelas UjiArrayList

ArrayList:
MAGENTA MERAH PUTIH BIRU COKLAT
Ukuran = 5; Kapasitas = 8
ArrayList memuat BIRU pada indeks 3.

ArrayList setelah pemanggilan HapusWarna:
MAGENTA COKLAT
Ukuran = 2; Kapasitas = 8
ArrayList tidak memuat BIRU.

Baris 27 memanggil metode TampilInformasi (baris 37-54) untuk menampilkan isi dari list. Metode ini menggunakan sebuah statemen foreach untuk menjelajah tiap elemen pada suatu ArrayList. Seperti yang telah didiskusikan, statemen foreach merupakan cara pintas untuk memanggil metode GetEnumerator dari kelas ArrayList dan untuk menggunakan sebuah enumerator untuk menjelajah elemen-elemen pada koleksi. Selain itu, baris 40 menyimpulkan bahwa tipe variabel iterasi adalah object karena kelas ArrayList merupakan kelas tak-generik dan menyimpan referensi-referensi ke object-object.

Anda menggunakan properti Count dan Capacity (baris 45) untuk menampilkan jumlah elemen terkini dan jumlah elemen maksimum yang dapat disimpan di dalam ArrayList tanpa perlu mengalokasikan memori tambahan. Keluaran pada Gambar 8.5 mengindikasikan bahwa ArrayList memiliki kapasitas 8.

Pada baris 47, Anda memanggil metode IndexOf untuk menentukan posisi dari stringBIRU” di dalam arrayList dan menyimpan hasilnya pada variabel lokal indeks. Metode IndexOf menghasilkan -1 jika elemen tidak ditemukan. Statemen if pada baris 49-53 memeriksa jika indeks adalah -1 untuk menentukan apakah arrayList memuat “BIRU”. Jika ya, akan ditampilkan indeks tersebut. ArrayList juga menyediakan metode Contains, yang menghasilkan true jika sebuah objek berada di dalam ArrayList, dan false jika sebaliknya. Metode Contains lebih direkomendasikan jika Anda tidak memerlukan indeks elemen yang dicari.

Setelah metode TampilInformasi selesai dieksekusi, Anda memanggil metode HapusWarna (baris 57-63) dengan dua ArrayList. Statemen for pada baris 61-62 beriterasi menjelajah listKedua. Baris 62 menggunakan sebuah indekser untuk mengakses sebuah elemen ArrayList, dengan menempatkan sepasang kurung siku ([]) setelah nama referensi ArrayList. Eksepsi ArgumentOutOfRangeException terjadi jika indeks yang ditentukan tidak lebih besar (atas sama dengan) dan kurang dari jumlah elemen yang sedang disimpan di dalam ArrayList (ditentukan oleh properti Count dari ArrayList).

Anda menggunakan indekser untuk mendapat tiap elemen listKedua, kemudian menghapus tiap elemen tersebut dari listPertama dengan metode Remove. Metode ini menghapus item tertentu dari sebuah ArrayList dengan melakukan pencarian linier dan hany menghapus kemunculan pertama dari objek tertentu.

Setelah pemanggilan terhadap HapusWarna, baris 33 menampilkan kembali isi dari list, yang menegaskan bahwa elemen-elemen dari hapusList telah terhapus.

8.4.2 Kelas Stack
Kelas Stack mengimplementasikan struktur data tumpukan. Pada Gambar 8.6 didemonstrasikan kelas koleksi Stack dari .NET Framework.

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
// Gambar 8.6: UjiStack.cs
// Mendemonstrasikan kelas Stack.
using System;
using System.Collections;

public class UjiStack
{
    public static void Main( string[] args )
    {
        Stack tumpukan = new Stack(); // menciptakan sebuah Stack kosong

        // menciptakan objek-objek yang akan disimpan pada tumpukan
        bool aBoolean = true;
        char aCharacter = '#';
        int anInteger = 34567;
        string aString = "hallo";

        // menggunakan metode Push untuk menempatkan item-item pada tumpukan
        tumpukan.Push( aBoolean );
        TampilStack( tumpukan );
        tumpukan.Push( aCharacter );
        TampilStack( tumpukan );
        tumpukan.Push( anInteger );
        TampilStack( tumpukan );
        tumpukan.Push( aString );
        TampilStack( tumpukan );

        // memeriksa elemen atas tumpukan
        Console.WriteLine( "Elemen atas dari tumpukan adalah {0}\n",
            tumpukan.Peek() );

        // menghapus item-item dari tumpukan
        try
        {
while ( true )
{
    object hapusObjek = tumpukan.Pop();
    Console.WriteLine( hapusObjek + " dihapus" );
    TampilStack( tumpukan );
} // akhir while
        } // akhir try
        catch ( InvalidOperationException eksepsi )
        {
            // jika eksepsi terjadi, menampilkan jejak tumpukan
            Console.Error.WriteLine( eksepsi );
        } // akhir catch
    } // akhir Main

    // menampilkan isi sebuah stack
    private static void TampilStack( Stack tumpukan )
    {
        if ( tumpukan.Count == 0 )
            Console.WriteLine( "tumpukan kosong\n" ); // tumpukan kosong
        else
        {
            Console.Write( "Tumpukan: " );

            // menjelajah tumpukan dengan statemen foreach
foreach ( var elemen in tumpukan )
    Console.Write( "{0} ", elemen ); // memanggil ToString

            Console.WriteLine( "\n" );
        } // akhir else
    } // akhir metode TampilStack
} // akhir kelas UjiStack

Tumpukan: True

Tumpukan: # True

Tumpukan: 34567 # True

Tumpukan: hallo 34567 # True

Elemen atas dari tumpukan adalah hallo

hallo dihapus
Tumpukan: 34567 # True

34567 dihapus
Tumpukan: # True

# dihapus
Tumpukan: True

True dihapus
tumpukan kosong

System.InvalidOperationException: Stack empty.
   at System.Collections.Stack.Pop()
   at UjiStack.Main(String[] args) in e:\Projects Visual C#\UjiStack\
      UjiStack\Program.cs:line 37

Direktif using pada baris 4 menghindarkan Anda untuk menggunakan kelas Stack dengan nama penuhnya (System.Collections.Stack). Baris 10 menciptakan sebuah Stack. Seperti yang Anda harapkan, kelas Stack memiliki metode Push dan Pop untuk melakukan operasi-operasi tumpukan dasar.

Metode Push memerlukan sebuah object sebagai argumen dan menyisipkannya ke atas Stack. Jika jumlah item pada Stack (properti Count) sama dengan kapasitasnya pada saat operasi Push dilakukan, maka Stack akan mengembang untuk mengakomodasi object-object baru. Baris 19-26 menggunakan metode Push untuk menambahkan empat elemen (sebuah bool, sebuah char, sebuah int, dan sebuah string) ke atas tumpukan dan memanggil metode TampilStack (baris 50-64) setelah tiap pemanggilan Push untuk menampilkan isi dari tumpukan. Perhatikan bahwa kelas Stack tak-generik ini hanya dapat menyimpan referensi-referensi ke object-object, jadi tiap item tipe-nilai secara implisit dikonversi sebelum ditambahkan ke Stack. (Namespace System.Collections.Generic menyediakan sebuah kelas Stack generik yang memiliki banyak metode dan properti. Versi ini mengeliminasi kebutuhan akan konversi tipe).

Metode TampilStack (baris 50-64) menggunakan properti Count dari kelas Stack (diimplementasikan untuk memenuhi kontrak dengan antarmuka ICollection) untuk mendapatkan jumlah elemen di dalam tumpukan. Jika tumpukan tidak kosong (yaitu, properti Count tidak sama dengan 0), maka Anda menggunakan statemen foreach untuk menjelajah tumpukan dan menampilkan isinya dengan secara implisit memanggil metode ToString pada tiap elemen. Statemen foreach secara implisit memanggil metode GetEnumerator dari kelas Stack, yang dapat Anda panggil secara eksplisit untuk menjelajah tumpukan melalui enumerator.

Metode Peek menghasilkan nilai dari elemen atas tumpukan tetapi tidak menghapus elemen tersebut dari Stack. Anda menggunakan Peek pada baris 30 untuk mendapatkan objek atas pada Stack, kemudian menampilkan objek tersebut dan secara implisit memanggil metode ToString pada objek tersebut. Eksepsi InvalidOperationException terjadi ketika Stack kosong ketika aplikasi memanggil Peek.

Metode Pop tidak memerlukan argumen. Metode ini menghapus objek yang sekarang berada di atas tumpukan dan manghasilkan nilai balik berupa objek tersebut. Loop pada baris 35-40 menghapus objek-objek dari tumpukan dan menampilkannya sampai tumpukan kosong. Ketika aplikasi memanggil Pop pada tumpukan kosong, maka eksepsi InvalidOperationException akan dilemparkan. Blok catch (baris 42-46) menampilkan eksepsi, secara implisit memanggil metode ToString pada eksepsi InvalidOperationException untuk mendapatkan pesan error dan jejak tumpukan.

Meskipun Gambar 7.6 tidak mendemonstrasikannya, kelas Stack memiliki metode Contains, yang menghasilkan true jika Stack memuat objek tertentu, dan menghasilkan false jika sebaliknya.

8.4.3 Kelas Hashtable
Ketika sebuah aplikasi menciptakan objek-objek dari tipe baru atau tipe yang sudah ada, ia perlu mengelola objek-objek tersebut secara efisien. Pengelolaan ini mencakup pengurutan dan pembacaan objek-objek. Pengurutan dan penarikan informasi dengan array hanya efisien jika beberapa aspek pada data secara langsung cocok dengan nilai kunci dan jika kunci unik. Jika Anda memiliki 100 karyawan dengan nomor KTP 9 dijit dan Anda ingin menyimpan dan menarik data karyawan menggunakan nomor KTP sebagai kuncinya, maka hal itu akan memerlukan 1 000 000 000 elemen karena terdapat satu milyar angka 9 dijit yang unik. Jika Anda memiliki sebuah array sebesar itu, maka Anda bisa menyimpan dan menarik rekaman informasi karyawan menggunakan nomor KTP sebagai indeks, tetapi hal itu merupakan tindakan pemborosan memori.

Banyak aplikasi mempunyai masalah seperti ini. Misalnya, kunci memiliki tipe yang salah (misalnya, kunci negatif), atau kunci dengan tipe benar tetapi tersebar secara jarang pada rentang yang sangat luas.

Apa yang diperlukan adalah skema berkecepatan-tinggi untuk mengkonversi kunci, seperti nomor KTP, menjadi indeks-indeks array yang unik. Kemudian, ketika aplikasi perlu menyimpan sesuatu, skema ini dapat mengkonversi kunci dengan cepat menjadi sebuah indeks dan rekaman informasi dapat disimpan pada lokasi tersebut di dalam array. Penarikan informasi dilakukan dengan cara yang sama. Ketika aplikasi memiliki sebuah kunci untuk menarik rekaman data, aplikasi hanya perlu mengkonversi kunci tersebut, yang menghasilkan indeks array dimana terdapat data yang disimpan di dalam array.

Skema yang dijelaskan di sini merupakan basis untuk suatu teknik yang disebut dengan hashing, dimana di dalamnya Anda menyimpan data pada sebuah struktur data yang dikenal dengan tabel hash.

Kekuarang pada skema ini terjadi ketika terdapat tubrukan (yaitu, dua kunci yang berbeda dikonversi menjadi indeks array yang sama). Karena Anda tidak dapat mengurutkan dua rekaman data yang berbeda pada ruang yang sama, Anda perlu mencari rumah baru alternatif bagi semua rekaman di luar rekaman pertama yang menghasilkan indeks array tertentu. Salah satu skema untuk melakukan ini adalah dengan menghashkan kembali kunci untuk menghasilkan kandidat indeks pada array.

Skema lain menggunakan satu hash untuk mencari kandidat indeks yang pertama. Jika indeks tersebut telah ditempati, maka sel-sel tetangganya (yang berurutan) diteliti secara linier sampai sebuah indeks yang kosong ditemukan. Penarikan informasi bekerja dengan cara yang sama, dimana kunci dihash sekali saja, indeks yang dihasilkan diperiksa untuk menentukan apakah ia memuat data yang diinginkan. Jika ya, pencarian selesai. Jika tidak, indeks-indeks tetangganya diteliti secara linier sampai data yang diinginkan ditemukan.

Faktor beban (load factor) mempengaruhi kinerja dari skema hashing. Faktor beban merupakan rasio dari jumlah objek yang disimpan pada tabel hash dengan jumlah sel (indeks) total pada tabel hash. Semakin tinggi rasio ini, peluang tubrukan cenderung meningkat.

Fungsi hash melakukan perhitungan untuk menentukan di mana menempatkan data pada tabel hash. Fungsi hash diterapkan terhadap pasangan kunci/nilai dari objek-objek. Kelas Hashtable dapat menerima sembarang object sebagai kunci. Karena itu, kelas object mendefinisikan metode GetHashCode, yang diwarisi oleh semua objek. Gambar 8.7 menggunakan sebuah Hashtable untuk menghitung jumlah kemunculan dari tiap kata pada sebuah string.

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
// Gambar 8.7: UjiTabelHash.cs
// Aplikasi menghitung jumlah kemunculan dari tiap kata pada sebuah string
// dan menyimpannya di dalam suatu tabel hash.
using System;
using System.Text.RegularExpressions;
using System.Collections;

public class UjiTabelHash
{
    public static void Main( string[] args )
    {
        // menciptakan tabel hash berdasarkan masukan pengguna
        Hashtable tabel = KumpulKata();

        // menampilkan isi tabel hash
        TampilTabelHash( tabel );
    } // akhir Main

    // menciptakan tabel hash dari masukan pengguna
    private static Hashtable KumpulKata()
    {
        Hashtable tabel = new Hashtable(); // menciptakan tabel hash baru

        Console.WriteLine( "Masukkan sebuah string: " ); // meminta masukan penguna
        string masukan = Console.ReadLine(); // membaca masukan

        // memecah teks masukan menjadi token-token
        string[] kata2 = Regex.Split( masukan, @"\s+" );

        // memproses kata-kata masukan
foreach ( var kata in kata2 )
{
    string kunciKata = kata.ToLower(); // membaca kata dalam huruf kecil
           
    // jika tabel hash memuat kata
    if ( tabel.ContainsKey( kunciKata ) )
    {
        tabel[ kunciKata ] = ( ( int ) tabel[ kunciKata ] ) + 1;
    } // akhir if
    else
        // menambah kata baru dengan hitungan satu pada tabel hash
        tabel.Add( kunciKata, 1 );
} // akhir foreach

        return tabel;
    } // akhir metode KumpulKata

    // menampilkan isi tabel hash
    private static void TampilTabelHash( Hashtable tabel )
    {
        Console.WriteLine( "\n Hashtable memuat:\n{0,-12}{1,-12}",
            "Kunci:", "Nilai:" );

        // menghasilkan keluaran untuk tiap kunci pada tabel hash
        // dengan menjelajah dengan properti Keys dengan statemen foreach
foreach ( var kunci in tabel.Keys )
    Console.WriteLine( "{0,-12}{1,-12}", kunci, tabel[ kunci ] );

        Console.WriteLine( "\nukuran: {0}", tabel.Count );
    } // akhir metode TampilTabelHash
} // akhir kelas UjiTabelHash

Masukkan sebuah string:
Klaten merupakan sebuah kota dengan kualitas air yang cukup baik bagi sebuah
kota kabupaten.

Hashtable memuat:
Kunci:      Nilai:
cukup       1
sebuah      2
merupakan   1
bagi        1
kabupaten.  1
dengan      1
yang        1
klaten      1
baik        1
kualitas    1
air         1
kota        2

ukuran: 12

Baris 4-6 memuat direktif using untuk namespace System (untuk kelas Console), namespace System.Text.RegularExpressions (untuk kelas Regex), dan namespace System.Collections (untuk kelas Hashtable). Kelas UjiTabelHash mendeklarasikan tiga metode static. Metode KumpulKata (baris 20-46) membaca sebuah string dan menghasilkan sebuah Hashtable dimana di dalamnya setiap nilai menyimpan jumlah perulangan kata pada string dan kata dipakai sebagai kunci. Metode TampilTabelHash (baris 49-60 menampilkan Hashtable yang dilewatkan kepadanya pada format kolom. Metode Main (baris 10-17) memanggil KumpulKata (baris 13), kemudian melewatkan Hashtable yang dihasilkan oleh KumpulKata kepada TampilTabelHash pada baris 16.

Metode KumpulKata (baris 20-46) memulai dengan menginisialisasi variabel lokal tabel dengan sebuah Hashtable baru (baris 22) yang memiliki beban faktor maksimum default sebesar 1. Ketika Hashtable meraih faktor beban yang ditetapkan, kapasitasnya ditambah secara otomatis. Baris 24-25 meminta pengguna memasukkan sebuah string dan program membacanya. Anda menggunakan metode static (Split) dari kelas Regex pada baris 28 untuk membagi string berdasarkan karakter-karakter spasi-putihnya. Ini menciptakan array yang memuat kata-kata, Kemudian Anda menyimpannya pada variabel lokal kata2.

Baris 31-43 menjelajah setiap elemen pada array kata2. Setiap kata dikonversi menjadi huruf kecil dengan metode ToLower dari kelas string, kemudian menyimpannya pada variabel kunciKata (baris 33). Kemudian baris 36 memanggil metode ContainsKey dari kelas Hashtable untuk menentukan apakah kata ada di dalam tabel hash atau tidak. Jika Hashtable tidak memuat sebuah entri untuk kata tersebut, baris 42 akan menggunakan metode Add dari kelas Hashtable untuk menciptakan sebuah entri pada di dalam tabel hash, dengan kata huruf kecil sebagai kunci dan sebuah objek yang memuat 1 sebagai nilai.

Jika kata telah menjadi sebuah kunci pada tabel hash, baris 38 akan menggunakan indekser dari Hashtable  untuk mendapatkan dan menetapkan nilai dari kunci terkait (jumlah kata) pada tabel hash. Anda pertama-tama melakukan downcast terhadap nilai yang didapatkan oleh aksesor get dari sebuah object menjadi sebuah int. Ini dilakukan agar Anda bisa menginkremennya sebesar 1. Kemudian, ketika Anda menggunakan aksesor set dari indekser untuk menugaskan nilai dari kunci tertentu, nilai terinkremen secara implisit downcast ulang sehingga ia dapat disimpan pada tabel hash.

Perhatikan bahwa pemanggilan aksesor get dari sebuah indekser Hashtable dengan suatu kunci yang tidak ada pada tabel hash akan menghasilkan referensi null. Penggunaan aksesor set dengan sebuah kunci yang tidak ada pada tabel hash akan menciptakan sebuah entri baru, seperti Anda menggunakan metode Add

Baris 45 menghasilkan tabel hash dan diberikan kepada metode Main, yang kemudian melewatkannya kepada metode TampilTabelHash (baris 49-60), yang menampilkan semua entri. Metode ini menggunakan properti readonly (Keys) pada baris 56 untuk mendapatkan sebuah ICollection yang memuat semua kunci. Karena ICollection mewarisi antarmuka IEnumerable, Anda dapt menggunakan koleksi ini pada statemen foreach pada baris 56-57 untuk menjelajah kunci-kunci pada tabel hash. Loop ini mengakses dan menampilkan setiap kunci dan nilainya pada tabel hash menggunakan variabel iterasi dan akses get dari tabel. Setiap kunci dan nilainya ditampilkan pada lebar bidang -12. Lebar bidang negatif mengindikasikan bahwa keluaran disejajarkan ke kiri. Tabel hash tidak terurut, jadi pasangan-pasangan kunci/nilai tidak ditampilkan dalam urutan tertentu. Baris 59 menggunakan properti Count dari kelas Hashtable untuk mendapatkan jumlah pasangan kunci/nilai pada Hashtable.

Baris 56-57 dapat pula menggunakan statemen foreach dengan objek Hashtable, menggantikan penggunaan properti Keys. Jika Anda menggunakan statemen foreach dengan sebuah objek Hashtable, maka variabel iterasi akan bertipe DictionaryEntry. Enumerator dari sebuah Hashtable (atau sembarang kelas lain yang mengimplementasikan IDictionary) menggunakan struktur DictionaryEntry untuk menyimpan pasangan-pasangan kunci/nilai. Struktur ini menyediakan properti Key dan Value untuk penarikan kunci dan nilai dari elemen terkini. Jika Anda tidak memerlukan kunci, maka kelas Hashtable juga menyediakan sebuah properti read-only Values yang mendapatkan sebuah ICollection dari semua nilai-nilai yang disimpan di dalam Hashtable.

8.5 Koleksi Generik
Namespace System.Collections.Generic memuat kelas-kelas generik yang memampukan Anda untuk menciptakan koleksi-koleksi dengan tipe spesifik. Seperti Anda lihat pada Gambar 8.2, banyak kelas merupakan versi-versi generik dari koleksi-koleksi tak-generik. Di sini, akan didemonstrasikan koleksi generik SortedDictionary dan LinkedList.

8.5.1 Kelas Generik SortedDictionary
Kamus merupakan istilah umum untuk sebuah koleksi yang memuat pasangan-pasangan kunci/nilai. Tabel hash merupakan salah satu cara dalam mengimplementasikan sebuah kamus. .NET Framework menyediakan beberapa implementasi atas kamus, baik generik maupun tak-generik, yang semuanya mengimplamentasikan antarmuka IDictionary. Aplikasi pada Gambar 8.8 merupakan modifikasi dari Gambar 8.7 yang menggunakan kelas SortedDictionary generik. Kelas ini tidak menggunakan sebuah tabel hash, tetapi menyimpan pasangan-pasangan kunci/nilai pada suatu pohon pencarian biner. Entri-entri pada SortedDictionary diurutkan pada pohon berdasarkan kunci. Ketika kunci mengimplementasikan antarmuka IComparable<T> generik, SortedDictionary menggunakan hasil-hasil dari metode CompareTo (dari antarmuka IComparable<T>) untuk mengurutkan kunci-kunci.

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
// Gambar 8.8: UjiSortedDictionary.cs
// Aplikasi menghitung jumlah kemunculan dari tiap kata pada sebuah string
// dan menyimpannya di dalam suatu kamus terurut generik.
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class UjiSortedDictionary
{
    public static void Main(string[] args)
    {
        // menciptakan kamus terurut berdasarkan masukan pengguna
        SortedDictionary<string, int> kamus = KumpulKata();

        // menampilkan isi kamus terurut
        TampilKamus(kamus);
    } // akhir Main

    // menciptakan tabel hash dari masukan pengguna
    private static SortedDictionary<string, int> KumpulKata()
    {
        // menciptakan sebuah kamus terurut yang baru
SortedDictionary<string, int> kamus =
    new SortedDictionary<string, int>();

        Console.WriteLine("Masukkan sebuah string: "); // meminta masukan penguna
        string masukan = Console.ReadLine(); // membaca masukan

        // memecah teks masukan menjadi token-token
        string[] kata2 = Regex.Split(masukan, @"\s+");

        // memproses kata-kata masukan
        foreach (var kata in kata2)
        {
            string kunciKata = kata.ToLower(); // membaca kata dalam huruf kecil

            // jika kamus memuat kata
            if (kamus.ContainsKey(kunciKata))
            {
                ++kamus[kunciKata];
            } // akhir if
            else
                // menambah kata baru dengan hitungan satu pada kamus
                kamus.Add(kunciKata, 1);
        } // akhir foreach

        return kamus;
    } // akhir metode KumpulKata

    // menampilkan isi tabel kamus
private static void TampilKamus<K, V>(
    SortedDictionary<K, V> kamus)
    {
        Console.WriteLine("\nKamus terurut memuat:\n{0,-12}{1,-12}",
            "Kunci:", "Nilai:");

        // menghasilkan keluaran untuk tiap kunci pada kamus terurut
        // dengan menjelajah dengan properti Keys dengan statemen foreach
        foreach (var kunci in kamus.Keys)
            Console.WriteLine("{0,-12}{1,-12}", kunci, kamus[kunci]);

        Console.WriteLine("\nukuran: {0}", kamus.Count);
    } // akhir metode TampilKamus
} // akhir kelas UjiSortedDictionary

Masukkan sebuah string:
Danau itu hening tanpa kebisingan, tanpa keserakahan manusia, keserakahan yang
menjijikkan.

Kamus terurut memuat:
Kunci:      Nilai:
danau       1
hening      1
itu         1
kebisingan, 1
keserakahan 2
manusia,    1
menjijikkan.1
tanpa       2
yang        1

ukuran: 9

Baris 6 memuat sebuah direktif using untuk namespace System.Collections.Generic, yang memuat kelas SortedDictionary. Kelas SortedDictionary generik mengambil dua argumen tipe, satu untuk menetapkan tipe kunci (yaitu, string) dan yang lain untuk tipe nilai (yaitu, int). Anda hanya perlu mengganti Hashtable pada baris 13 dan baris 23-24 dengan SortedDictionary<string, int> untuk menciptakan sebuah kamus yang memuat nilai-nilai int yang dikunci dengan string-string. Sekarang, kompiler dapat memeriksa dan memberitahu Anda jika Anda mencoba menyimpan sebuah objek dengan tipe salah pada kamus. Selain itu, karena kompiler sekarang mengetahui bahwa struktur data ini memuat nilai-nilai int, tidak diperlukan lagi downcast pada baris 40. Ini memampukan baris 40 untuk menggunakan notasi inkremen prefiks (++) yang lebih singkat.

Metode statis TampilKamus (baris 51-63) telah dimodifikasi agar menjadi generik. Metode ini memerlukan parameter tipe K dan V. Parameter-parameter ini digunakan pada baris 52 untuk mengindikasikan bahwa TampilKamus memerlukan sebuah SortedDictionary dengan kunci-kunci dengan tipe K dan nilai-nilai dengan tipe V. Anda menggunakan parameter tipe K kembali pada baris 59 sebagai tipe dari kunci iterasi. Penggunaan generik ini merupakan contoh yang menarik dari pendaur-ulangan kode. Jika Anda memutuskan untuk mengubah aplikasi agar menghitung jumlah kemunculan dari tiap karakter yang muncul pada sebuah string, maka metode TampilKamus dapat menerima sebuah argumen dengan tipe SortedDictionary<char, int> tanpa modifikasi. Pasangan-pasangan kunci yang ditempilkan sekarang diurutkan berdasarkan kunci, seperti ditampilkan pada Gambar 8.8.

8.5.2 Kelas LinkedList Generik
Kelas LinkedList generik merupakan sebuah senarai berantai ganda. Anda dapat menavigasi senarai tersebut ke depan dan ke belakang dengan simpul-simpul dari kelas LinkedListNode generik. Setiap simpul memuat properti Value dan properti-properti read-only Previous dan Next. Tipe dari properti Value cocok dengan parameter tipe pada LinkedList karena ia memuat data yang disimpan pada sebuah simpul. Properti Previous mendapatkan sebuah referensi yang menunjuk ke simpul sebelumnya pada senarai berantai (atau referensi null jika simpul merupakan simpul pertama pada senarai). Sama halnya, properti Next mendapatkan sebuah referensi yang menunjuk ke simpul berikutnya pada senarai berantai (atau referensi null jika simpul merupakan simpul pertama pada senarai). Beberapa manipulasi terhadap senarai berantai didemonstrasikan pada Gambar 8.9.

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Gambar 8.9: UjiLinkedList.cs
// Menggunakan LinkedLists.
using System;
using System.Collections.Generic;

public class UjiLinkedList
{
    private static readonly string[] warna2 = { "hitam", "kuning",
        "hijau", "biru", "violet", "perak" };
    private static readonly string[] warna22 = { "emas", "putih",
        "coklat", "biru", "abu2" };

    // memanipulasi objek-objek LinkedList
    public static void Main( string[] args )
    {
        LinkedList< string > list1 = new LinkedList< string >();

        // menambahkan elemen-elemen pada senarai berantai pertama
        foreach ( var warna in warna2 )
            list1.AddLast( warna );

        // menambahkan elemen-elemen pada senarai berantai kedua lewat konstruktor
        LinkedList< string > list2 = new LinkedList< string >( warna22 );

        Sambung( list1, list2 ); // menyambung list2 ke list1
        TampilSenarai( list1 ); // menampilkan elemen-elemen list1

        Console.WriteLine( "\nMengkonversi string-string list1 menjadi huruf besar\n" );
        KeStringHurufBesar( list1 ); // mengkonversi menjadi string huruf besar
        TampilSenarai( list1 ); // menampilkan elemen-elemen list1

        Console.WriteLine( "\nMenghapus string-string antara HITAM dan COKLAT\n" );
        HapusAntarItem( list1, "HITAM", "COKLAT" );

        TampilSenarai( list1 ); // menampilkan elemen-elemen list1
        TampilSenaraiTerbalik(list1); // menampilkan senarai dalam urutan terbalik
    } // akhir Main

    // menampilkan isi senarai berantai
    private static void TampilSenarai< T >( LinkedList< T > list )
    {
        Console.WriteLine( "Senarai berantai: " );

foreach ( T nilai in list )
    Console.Write( "{0} ", nilai );

        Console.WriteLine();
    } // akhir metode TampilSenarai

    // menyambung list kedua diujung list pertama
    private static void Sambung< T >( LinkedList< T > list1,
        LinkedList< T > list2 )
    {
// menyambung senarai-senarai dengan menyalin nilai-nilai elemen
// secara berurutan dari senarai kedua ke senarai pertama
    foreach ( T nilai in list2 )
        list1.AddLast( nilai ); // menambahkan simpul baru
    } // akhir metode Sambung

    // mencari objek-objek string dan mengkonversinya menjadi huruf besar
private static void KeStringHurufBesar( LinkedList< string > list )
{
    // menjelajah senarai menggunakan simpul-simpul
    LinkedListNode< string > simpulSekarang = list.First;

    while ( simpulSekarang != null )
    {
        string warna = simpulSekarang.Value; // mendapatkan nilai pada simpul
        simpulSekarang.Value = warna.ToUpper(); // konversi ke huruf besar

        simpulSekarang = simpulSekarang.Next; // mendapatkan simpul berikutnya
    } // akhir while
} // akhir metode KeStringHurufBesar

    // menghapus item-item senarai antara dua item yang diberikan
    private static void HapusAntarItem< T >( LinkedList< T > list,
        T itemAwal, T itemAkhir )
    {
        // mendapatkan simpul-simpul yang terkait dengan item awal dan item akhir
LinkedListNode< T > simpulSekarang = list.Find( itemAwal );
LinkedListNode< T > simpulAkhir = list.Find( itemAkhir );

        // menghapus item-item setelah item awal
        // sampai ditemukan item terakhir atau sampai akhir senarai berantai
        while ( ( simpulSekarang.Next != null ) &&
            ( simpulSekarang.Next != simpulAkhir ) )
        {
            list.Remove( simpulSekarang.Next ); // menghapus simpul berikutnya
        } // akhir while
    } // akhir metode HapusAntarItem

    // menampilkan senarai berantai secara terbalik
    private static void TampilSenaraiTerbalik< T >( LinkedList< T > list )
    {
        Console.WriteLine( "Senarai terbalik:" );

        // menjelajah senarai menggunakan simpul-simpul
LinkedListNode< T > simpulSekarang = list.Last;

while ( simpulSekarang != null )
{
    Console.Write( "{0} ", simpulSekarang.Value );
    simpulSekarang = simpulSekarang.Previous; // simpul sebelumnya
} // akhir while

        Console.WriteLine();
    } // akhir metode TampilSenaraiTerbalik 
} // akhir kelas UjiLinkedList

Senarai berantai:
hitam kuning hijau biru violet perak emas putih coklat biru abu2

Mengkonversi string-string pada list1 menjadi huruf besar

Senarai berantai:
HITAM KUNING HIJAU BIRU VIOLET PERAK EMAS PUTIH COKLAT BIRU ABU2

Menghapus string-string antara HITAM dan COKLAT

Senarai berantai:
HITAM COKLAT BIRU ABU2
Senarai terbalik:
ABU2 BIRU COKLAT HITAM

Direktif using pada baris 4 memampukan Anda untuk menggunakan kelas LinkedList dengan nama tak-penuh. baris 16-23 menciptakan dua LinkedList, list1 dan list2, yang memuat string-string dan mengisinya dengan isi array warna2 dan warna22. LinkedList merupakan sebuah kelas generik yang memiliki satu parameter tipe. Dua cara untuk mengisi senarai berantai telah didemonstrasikan. Pada baris 19-20, Anda menggunakan statemen foreach dan metode AddLast untuk mengisi list1. Metode AddaLast menciptakan sebuah LinkedListNode yang baru (dengan nilai yang diberikan melalui properti Value) dan menyambungkan simpul ini ke ujung akhir senarai berantai. Metode AddFirst dipakai untuk menyisipkan sebuah simpul di awal senarai berantai. Baris 32 memanggil konstruktor yang mengambil sebuah parameter IEnumerable<string>.

Semua array secara implisit mewarisi antarmuka generik, IList dan IEnumerable, dengan tipe array sebagai argumen tipe, sehingga array string, warna22, mengimplementasikan IEnumerable<string>. Parameter tipe dari IEnumerable generik ini cocok dengan parameter tipe dari objek LinkedList generik. Pemanggilan konstruktor ini menyalin isi dari array warna22 ke list2.

Baris 25 memanggil metode Sambung generik (baris 51-58) untuk menempatkan semua elemen dari list2 ke ujung dari list1. Baris 26 memanggil metode TampilSenarai (baris 40-48) untuk menampilkan isi dari list1. Baris 29 memanggil metode KeStringHurufBesar (baris 61-73) untuk mengkonversi setiap elemen string menjadi huruf besar, kemudian baris 30 memanggil TampilSenarai kembali untuk menampilkan string-string termodifikasi. Baris 33 memanggil metode HapusAntarItem (baris 76-90) untuk menghapus elemen-elemen antara “HITAM” dan “COKLAT”. Baris 35 menampilkan senarai kembali, kemudian baris 36 memanggil metode TampilSenaraiTerbalik (baris 93-107) untuk menampilkan senarai dalam tatanan terbalik.

Metode Sambung generik (baris 51-58) beriterasi menjelajah list2 dengan sebuah statemen foreach dan memanggil metode AddLast untuk menempatkan setiap nilai ke ujung akhir dari list1. Enumerator dari kelas LinkedList beriterasi menjelajah nilai-nilai pada simpul-simpul, bukan pada simpul-simpul itu sendiri, jadi variabel iterasi bertipe T. Perhatikan bahwa hal ini menciptakan sebuah simpul baru pada list1 untuk setiap simpul pada list2. Satu LinkedListNode tidak bisa menjadi anggota dari lebih dari satu LinkedList. Jika Anda ingin data yang sama dimiliki oleh lebih dari satu LinkedList, maka Anda harus melakukan penyalinan simpul pada tiap senarai untuk menghindari eksepsi InvalidOperationException.

Metode TampilSenarai generik (baris 40-48) menggunakan sebuah statemen foreach untuk beriterasi pada nilai-nilai di dalam sebuah LinkedList, dan menampilkannya. Metode KeStringHurufBesar (baris 61-73) mengambil sebuah senarai berantai yang memuat string-string dan mengkonversi setiap nilai string tersebut menjadi huruf besar. Metode ini mengganti string-string yang disimpan pada senarai, jadi Anda tidak dapat menggunakan enumerator (melalui statemen foreach) seperti pada dua metode sebelumnya. Melainkan, Anda mendapatkan LinkedListNode pertama melalui properti First (baris 64), dan menggunakan sebuah statemen while untuk beriterasi menjelajah senarai (baris 66-72). Setiap iterasi pada statemen while mendapatkan dan memperbarui isi dari simpulSekarang melalui properti Value, menggunakan metode ToUpper dari kelas string untuk menciptakan versi huruf besar dari string warna2. Di akhir tiap iterasi, Anda memajukan simpul sekarang ke simpul berikutnya pada senarai berantai dengan menugaskan simpulSekarang kepada simpul yang diperoleh oleh properti Next-nya sendiri (baris 71). Properti Next pada simpul terakhir bernilai null, jadi ketika statemen while beriterasi melewati akhir senarai berantai, loop akan berhenti.

Perhatikan bahwa tidak masuk akal untuk mendeklarasikan KeStringHurufBesar sebagai metode generik, karena ia menggunakan metode-metode spesifik string pada nilai tiap simpul. Metode TampilSenarai (baris 48-48) dan Sambung (baris 51-58) tidak menggunakan satupun metode spesifik string, jadi keduanya dapat dideklarasikan dengan parameter tipe generik untuk mempromosikan pendaur-ulangan kode.

Metode HapusAntarItem generik (baris 76-90) menghapus sebuah rentang item antara dua simpul. Baris 80-81 mendapatkan dua simpul batas pada rentang menggunakan metode Find. Metode ini melakukan pencarian linier pada senarai dan menghasilkan simpul pertama yang memuat nilai yang sama dengan argumen yang dilewatkan. Metode Find menghasilkan null jika nilai tidak ditemukan.

Statemen while pada baris 85-89 menghapus semua elemen di antara simpulSekarang dan simpulAkhir. Pada tiap iterasi loop, Anda menghapus simpul yang berada setelah simpulSekarang dengan memanggil metode Remove (baris 88). Metode Remove mengambil sebuah LinkedListNode, mengeluarkan simpul tersebut dari LinkedList, dan memperbaiki semua referensi yang ada disekitar simpul tersebut. Setelah pemanggilan Remove, properti Next dari simpulSekarang sekarang mendapatkan simpul yang berada setelah simpul yang baru saja dihapus, dan bahwa properti Prevous dari simpul tersebut sekarang mendapatkan simpulSekarang. Statemen while berlanjut beriterasi sampai tidak ada lagi simpul yang tertinggal di antara simpulSekarang dan simpulAkhir, atau sampai simpulSekarang merupakan simpul terakhir pada senarai berantai.

Metode TampilSenaraiTerbalik (baris 93-107) menampilkan senarai berantai secara mundur dengan menavigasi simpul-simpul secara manual. Baris 98 mendapatkan elemen terakhir pada senarai melalui properti Last dan menyimpannya di dalam simpulSekarang. Statemen while pada baris 100-104 menjelajah senarai secara mundur dengan memajukan referensi simpulSekarang ke simpul sebelumnya pada akhir tiap iterasi, kemudian keluar loop ketika Anda melewati awal senarai. Perhatikan bagaimana kode ini mirip dengan kode pada baris 64-72, yang beriterasi menjelajah senarai berantai dimulai dari awal sampai akhir senarai.








No comments:

Post a Comment