This Blog

Syndication

News

Awards

Microsoft MVP Logo
Grab this badge here!

Certificates

Transcript ID#: 6724122
Brainbench ASP.NET Certificate
 
Brainbench ASP.NET 2.0 Certificate
 
Brainbench .NET Framework 2.0 Certificate

Ahmad Masykur

var myObject = { "languages" : [ "C#", "VB.NET", "ASP.NET", "Javascript", "SDCC", "Assembler" ] }; //

July 2007 - Posts

  • Menulis Alamat Email yang Aman dari Spam

    Spam adalah penyalahgunaan dalam penampilan berita elektronik untuk pengiriman berita iklan dan keperluan lainnya yang mengakibatkan ketidaknyamanan bagi para pengguna web (http://id.wikipedia.org/wiki/Spam). Sering kali kita mendapatkan email spam.

    Dari mana email spam itu berasal? Bagaimana si pengirim spam mengetahui alamat email kita? Ada banyak cara yang dapat dilakukan oleh pengirim email spam untuk mendapatkan alamat email. Salah satu cara yang banyak digunakan adalah dengan mencari pola email dari halaman-halaman yang dipublikasikan. Pola emal sangat unik yaitu nama@domain sehingga dengan mudah dikenali bahwa itu adalah alamat email. Dengan menggunakan robot untuk membaca halaman-halaman web yang saling berkaitan, digunakan regular expression sederhana maka semua alamat email dalam satu halaman dapat diambil oleh si pengirim spam.

    Untuk menanggulangi pembacaan alamat email oleh spam dapat dilakukan dengan berbagai cara, salah satu cara adalah dengan mengubah tulisan yang masih bisa dipahami oleh manusia tapi tidak terbaca oleh robot. Perubahan tulisan yang umum digunakan adalah emailku@domainku.web.id ditulis menjadi:

    • emailku[at]domainku[dot]web[dot]id
    • emailkuNOSPAM@domainku.web.id (dengan menambahkan keterangan "hilangkan NOSPAM")
    • emailku^at^domainku.web.id
    • dan lain sebagainya.

    Dengan merubah penamaan alamat email, minimal mengurangi jumlah robot yang bisa membaca alamat email yang dipublikasikan tersebut. Namun robot yang lebih cerdas juga dapat membaca pola-pola yang umum tersebut. Salah satu trik untuk melindungi publikasi alamat email dari spam robot adalah menggunakan Javascript karena biasanya robot sangat sulit untuk menterjemahkan Javascript.

    // Opens the caller's default e-mail client
    // with the subject filled if specified.
    function SafeMail(name, domain, subject)
    {
      if (subject != null)
        subject = "?subject=" + subject;
      location.href="mailto:" + name + "@" + domain + subject;
    }

    Script di atas sebenarnya hanya untuk menyembunyikan pola penulisan mailto:nama@domain dengan sebuah fungsi Javascript. Fungsi tersebut digunakan untuk menggabungkan potongan-potongan pola email menjadi pola email yang sebenarnya.

    Berikut contoh penulisan pada halaman html untuk menyembunyikan pola email dengan fungsi di atas.

    <h1>About the author</h1> 
    <div class="vcard">
    <span class="fn">Ahmad Masykur</span> is a software developer at <a href="http://www.bataviasoft.com/">
    <span class="org">Bataviasoft</span></a> 
    <span class="adr"><span class="locality">Jakarta</span> 
    <span class="country-name">Indonesia</span></span>.
    </div> 
    <!-- Email -->
    <div style="float:right;">
    <a href="BLOCKED SCRIPTSafeMail('ahmad', 'masykur.web.id', 'Menulis Alamat Email yang Aman dari Spam')">E-mail me</a>
    </div>
    Dengan cara tersebut, yang ditampilkan di halaman hanyalah tulisan E-mail me. Pada waktu link di-klik, otomatis akan membuka email-client yang telah tersisi alamat email dan subject.
    Share this post: | | | |
  • Membuat HttpHandler untuk Resize Image

    Ketika membuat sebuah galeri foto, sering kali harus membuat beberapa versi ukuran foto. Foto-foto kecil ditampilkan secara berjajar. Ketika foto diklik, akan muncul foto dengan ukuran besar. Pada waktu slide-show, maka foto ukuran sedang yang ditampilkan. Untuk membuat foto dengan ukuran yang bermacam-macam dapat dilakukan dengan merubah ukuran secara run-time.

    Berikut adalah class untuk melakukan resize ukuran image.

    public class PhotoManager {
        public static byte[] ResizeImageFile(byte[] imageFile, int targetSize) {
            using (System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile))) {
                Size newSize = CalculateDimensions(oldImage.Size, targetSize);
                using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb)) {
                    using (Graphics canvas = Graphics.FromImage(newImage)) {
                        canvas.SmoothingMode = SmoothingMode.AntiAlias;
                        canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
                        canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
                        MemoryStream m = new MemoryStream();
                        newImage.Save(m, ImageFormat.Jpeg);
                        return m.GetBuffer();
                    }
                }
            }
        }
    
        private static Size CalculateDimensions(Size oldSize, int targetSize) {
            Size newSize = new Size();
            if (oldSize.Height > oldSize.Width) {
                newSize.Width = (int)(oldSize.Width * ((float)targetSize / (float)oldSize.Height));
                newSize.Height = targetSize;
            } else {
                newSize.Width = targetSize;
                newSize.Height = (int)(oldSize.Height * ((float)targetSize / (float)oldSize.Width));
            }
            return newSize;
        }
    }

    Dalam kode di atas, targetSize merupakan target panjang image yang akan diperoleh. Untuk mengirimkan gambar ke client, dibutuhkan sebuah handler.

    Berikut adalah handler mengirimkan image ke client. Kode dibawah disimpan dalam file dengan extention .ashx yang akan dikerjakan oleh web server sebagai sebuah web handler.

    <%@ WebHandler Language="C#" Class="Handler" %>
    
    using System;
    using System.IO;
    using System.Web;
    
    public class Handler : IHttpHandler {
    
    	public bool IsReusable {
    		get {
    			return true;
    		}
    	}
    	
    	public void ProcessRequest (HttpContext context) {
    		// Set up the response settings
    		context.Response.ContentType = "image/jpeg";
    		context.Response.Cache.SetCacheability(HttpCacheability.Public);
    		context.Response.BufferOutput = false;
    		// Get photo from file system
            string filename = context.Request.QueryString("FileName");
            if (filename != null)
            {
                using (FileStream fileStream = new FileStream(context.Server.MapPath("images\\" + filename)))
                {
                    using (BinaryReader reader = new BinaryReader(fileStream))
                    {
                        byte[] imageFile = reader.ReadBytes(fileStream.Length);
                        byte[] stream;
                        stream = (context.Request.QueryString("Size") != null) ? PhotoManager.ResizeImageFile(imageFile, int.Parse(context.Request.QueryString("Size"))) : imageFile;
                        // Write image stream to the response stream
                        const int buffersize = 1024 * 16;
                        byte[] buffer = new byte[buffersize];
                        int count = stream.Read(buffer, 0, buffersize);
                        while (count > 0)
                        {
                            context.Response.OutputStream.Write(buffer, 0, count);
                            count = stream.Read(buffer, 0, buffersize);
                        }
                    }
                }
            }
        }
    }

    Pada halaman web tinggal panggil handler yang telah dibuat dengan querystring nama file dan ukuran image.

    <img src="Handler.ashx?FileName=myimage.jpg&Size=320"style="border:4px solid white" alt="My Image" />
    
    Share this post: | | | |
    Posted Jul 30 2007, 12:24 AM by cahnom with no comments
    Filed under:
  • Memperbanyak Link yang Merujuk ke Blog Anda

    Sekitar satu bulan yang lalu, saya memanfaatkan waktu luang untuk mengadaptasikan theme Cogitation dari DotText ke theme BlogEngine.NET. Hari ini, saya coba ketik beberapa keyword yang berhubungan dengan blog saya di search engine. Ternyata rangking blog saya meningkat cukup signifikan dibandingkan bulan lalu. Dan ketika saya ketik keyword yang spesifik berhubungan dengan theme yang saya bikin, ternyata sudah banyak orang yang pake theme tersebut. Blog yang saya bikin dengan mesin BlogEngine.NET dapat dilihat di http://www.masykur.web.id

    Bagi yang berminat untuk meningkatkan rangking blognya di search engine, bisa coba buat theme BlogEngine.NET. Saat ini project BlogEngine masih menerima theme tambahan yang akan dibundel dalam paket source dan binary-nya. Situs resmi BlogEngine.NET dapat dilihat di http://www.dotnetblogengine.net/

    Share this post: | | | |
    Posted Jul 29 2007, 09:21 PM by cahnom with no comments
    Filed under:
  • BlogEngine.NET 1.1 is now available

    Sudah beberapa hari dengan kesibukan project, tidak sempat untuk mengecek versi terbaru dari BlogEngine.NET. Hari saya coba lihat di http://www.codeplex.com/blogengine ternyata sudah ada release 1.1. Beberapa fitur tambahan di release ini adalah:

    • Internationalization (Telah tersedia 17 bahasa, Bahasa Indonesia belum masuk dalam release ini tapi telah masuk di source code terakhir).
    • Control-control baru (Ratings, Recent Comments, Recent Posts, Page List yang merupakan update dari versi sebelumnya)
    • Dukungan MS SQL Server yang telah diperbaiki.
    • Dukungan Windows Live Write termasuk dukungan tag.
    • Perbaikan kinerja dan lebih stabil dari versi sebelumnya.
    • Beberapa tambahan kontrol Admin
    • Beberapa theme tambahan.
    Share this post: | | | |
    Posted Jul 29 2007, 11:33 AM by cahnom with no comments
    Filed under:
  • DotNetNuke version 4.5.4 introduces SSL support!

    Saat tulisan ini dipublikasikan, DNN telah hadir dengan versi terbaru yaitu 4.5.5. Semenjak versi 4.5.4, DNN menambahkan fitur untuk dukungan SSL. Untuk informasi lebih lanjut silakan cek di website resmi DotNetNuke di www.dotnetnuke.com

    Share this post: | | | |
    Posted Jul 28 2007, 08:26 PM by cahnom with no comments
    Filed under:
  • DotNetNuke di Windows Vista (IIS 7)

    Jika kita install DotNetNuke pada Windows Vista (IIS 7), beberapa module tidak bisa berjalan dengan baik pada waktu melakukan Edit, Add File atau lainnya. Jika kita melakukan Edit atau Add File, muncul pesan error sebagai berikut.

    Server Error in Application "Default Web Site/DotNetNuke"


    HTTP Error 404.11 - Not Found

    Description: The request filtering module is configured to deny a request that contains a double escape sequence.

    Error Code: 0x00000000

    Notification: BeginRequest

    Module: RequestFilteringModule

    Requested URL: http://wks-phil:80/dotnetnuke/Gallery/tabid/54/ctl/AlbumEdit/mid/373/path/+/currentstrip/1/Default.aspx

    Physical Path: C:\inetpub\wwwroot\DotNetNuke\Gallery\tabid\54\ctl\AlbumEdit\mid\373\path\+\currentstrip\1\Default.aspx

    Logon User: Not yet determined

    Logon Method: Not yet determined

    Handler: PageHandlerFactory-ISAPI-2.0

    Most likely causes:

    • The request contained a double escape sequence and request filtering is configured on the Web server to deny double escape sequences.

    What you can try:

    • Verify the configuration/system.webServer/security/requestFiltering@allowDoubleEscaping setting in the applicationhost.config or web.confg file.

     

    More Information...

    Untuk memperbaiki error tersebut, buka file web.config DotNetNuke menggunakan teks editor seperti Notepad. Tambahkan kode berikut setelah </configSections>

    <configuration>
    <configSections>
    ...
    </configSections>
        <system.webServer>
            <security>
                <requestFiltering allowDoubleEscaping="True"/>
            </security>
        </system.webServer>
    ...
    </configuration>

    Simpan web.config dan restart IIS dengan perintah iisreset pada command prompt.

    DotNetNuke bisa dijalankan sebagaimana mestinya.

    Share this post: | | | |
    Posted Jul 28 2007, 05:57 PM by cahnom with no comments
    Filed under:
  • Windows Vista Aero vs Linux Ubuntu Beryl

    Coba lihat video perbandingan antara Windows Vista Aero vs Linux Ubuntu Beryl. Jika di blog ini tidak bisa dilihat, silakan lihat di
    http://www.masykur.web.id/post/Windows-Vista-Aero-vs-Linux-Ubuntu-Beryl.aspx atau
    http://youtube.com/watch?v=xC5uEe5OzNQ

    Bandingkan! Keren mana?

    Share this post: | | | |
  • Upload dan Download File

    Upload file di ASP.NET dapat menggunakan ASP.NET Web Control FileUpload. Untuk implementasi upload tinggal pasang control FileUpload dan satu Button.

    <asp:FileUpload ID="fileUpload" runat="server" Width="300" />
    <asp:Button id="btnUpload" runat="server" Text="Upload" />

    Pada codebehind, buat handler untuk btnUpload.Click

    Private Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnUpdate.Click
        Try
            Dim objFile As FileInfo = New FileInfo
    
            If fileUpload.PostedFile Is Nothing Then
                Exit Sub
            End If
            If Not fileUpload.HasFile Then
                Exit Sub
            End If
    
            Dim path As String = Me.MapPath("files/")
            path = path.Replace("/", "\")
            If Not Directory.Exists(path) Then
                Directory.CreateDirectory(path)
            End If
            fileUpload.SaveAs(path + fileUpload.PostedFile.FileName)
    
        Catch exc As Exception    
            'Exception code here
        End Try
    End Sub

    Untuk download file, kita bisa menggunakan Response untuk mengirimkan data ke client. Misalkan ada sebuat tombol untuk download, handler tombol tersebut dapat ditulis sebagai berikut.

    Dim path As String = Me.MapPath("files/")
    path = path.Replace("/", "\")
    Dim namafile As String = "namafile.ext"
    Response.Clear()    'Bersihkan buffer stream
    'Tambahkan header namafile
    Response.AppendHeader("content-disposition", "attachment; filename=" + namafile)
    Response.WriteFile(path + namafile)
    Response.End()
    Response.Close()

    Jika file yang diupload perlu diolah terlebih dahulu atau disimpan dalam database, maka file tersebut dapat dibaca menggunakan BinaryReader. Berikut contoh sederhana untuk melakukan enkripsi sebelum file disimpan di filesystem atau database.

    Private Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnUpdate.Click
        Try
            Dim objFile As FileInfo = New FileInfo
    
            If fileUpload.PostedFile Is Nothing Then
                Exit Sub
            End If
            If Not fileUpload.HasFile Then
                Exit Sub
            End If
    
            Dim path As String = Me.MapPath("files/")
            path = path.Replace("/", "\")
            If Not Directory.Exists(path) Then
                Directory.CreateDirectory(path)
            End If
            Dim uploadedStream As New BinaryReader(fileUpload.PostedFile.InputStream)
    	  Dim bytesStream As Byte() = uploadedStream.ReadBytes(fileUpload.PostedFile.ContentLength)
            'Tutup stream
            uploadedStream.Close()
            'Panggil fungsi enkripsi (contoh)
            Dim cipherStream As Byte() = Encrypt(bytesStream)
            'Simpan hasil enkripsi dalam filesystem
            Using _fileStream As New FileStream(path + fileUpload.FileName, FileMode.Create, FileAccess.Write)
                _fileStream.Write(cipherStream, 0, cipherStream.Length)
                _fileStream.Flush()
                _fileStream.Close()
            End Using
        Catch exc As Exception    
            'Exception code here
        End Try
    End Sub

    Jika file telah dienkripsi, maka pada waktu download file tersebut juga harus didekripsi sebelum dikirimkan ke client.

    path = path.Replace("/", "\") 
    Dim namafile As String = "namafile.ext" 
    'Buka stream
    Dim _fileStream As New FileStream(path + namafile, FileMode.Open, FileAccess.Read)
    Dim _binReader As New BinaryReader(_fileStream)
    Dim _cipherStream As Byte() = _binReader.ReadBytes(_fileStream.Length)
    _binReader.Close()
    _fileStream.Close()
    'Panggil fungsi dekripsi (contoh)
    Dim _plainStream As Byte() = Decrypt(_cipherStream)
    'Kirim stream ke client
    Response.Clear() 'Bersihkan buffer stream 
    'Tambahkan header namafile 
    Response.AppendHeader("content-disposition", "attachment; filename=" + namafile) 
    Response.BinaryWrite(plainStream)
    Response.Flush()
    Response.End() 
    Response.Close()

    Semoga bermanfaat

    Share this post: | | | |
    Posted Jul 26 2007, 10:46 PM by cahnom with 3 comment(s)
    Filed under:
  • Prayer Time Gadget

    Bagi orang-orang yang bekerja di kantor sering kali tidak mendengar seruan Adzan sehingga lalai akan kewajibannya menunaikan ibadah sholat. Beberapa hari yang lalu saya menemukan salah satu gadget menarik yang patut dipasang oleh setiap muslim di Windows Vista. Gadget tersebut adalah Prayer Time yang dapat diambil di http://x.vista.gallery.microsoft.com/liveItemDetail.aspx?li=e0f661db-04f7-498a-9b37-2446dffeba01&bt=1&pl=1

    Gadget ini sangat bermanfaat bagi kita untuk mengingatkan akan waktu sholat. Lima belas menit sebelum waktu sholat, akan dibacakan beberapa Ayat Al-Qur'an sehingga kita bisa bersiap-siap untuk mengambil air wudhu. Algoritma perhitungan waktu sholat yang dipakai sangat akurat. Pemilihan suara adzan juga sangat beragam.

    Bagi yang selama ini sering tertinggal atau terlambat dalam menunaikan sholat, saya anjurkan untuk memasang gadget ini di komputer Windows Vista Anda.

    Share this post: | | | |
  • Prayer Time Algorithm

    Berikut ini algoritma sederhana perhitungan waktu sholat untuk daerah Jakarta dan sekitarnya. Untuk daerah lain tinggal disesuaikan latitude dan longitude-nya. Algoritma diambil dari Prayer Time Algorithm di http://www.islamway.com/flashes/7/PrayerTimes.zip

    <script language="javascript" type="text/javascript">
    <!--
        var today = new Date();
        var J = 0;                                  // Day of year
        var H = 4;                                  // The height above sea level in meters
        var D = 0;                                  // Solar Declination (Degrees)
        var T = 0;                                  // Equation of Time (Minutes)
        var Gd = 15;                                // Dawn’s Twilight Angle (15°-19°)
        var Gn = 15;                                // Night’s Twilight Angle (15°-19°)
        var B = -6.2;                               // Latitude (Degrees)
        var L = 106.8;                              // Longitude (Degrees)
        var TZ = 7;                                 // Time Zone (Hours)
        var R = 0;                                  // Reference Longitude (Degrees)  
        var Sh = 1;                                 // Sh=1 (Shafii) - Sh=2 (Hanafi)
        var firstYear = new Date();
        
        firstYear.setMonth(0);
        firstYear.setDate(1);
        J = (today - firstYear) / 86400000;
    	var beta = 2 * Math.PI * J / 365            // Year angle
    	D = (180/Math.PI) * (0.006918 - (0.399912 * Math.cos(beta)) + (0.070257 * Math.sin(beta)) - (0.006758 * Math.cos(2 * beta)) + (0.000907 * Math.sin(2 * beta)) - (0.002697 * Math.cos(3 * beta)) + (0.001480 * Math.sin(3 * beta)));
    	T = 229.18 * (0.000075 + (0.001868 * Math.cos(beta)) - (0.032077 * Math.sin(beta)) - (0.014615 * Math.cos(2 * beta)) - (0.040849 * Math.sin(2 * beta)));
        R = 15 * TZ;
        var G = 18;
        var Z = 12 + ((R - L) / 15) - (T / 60);
        var U =  (180 / (15 * Math.PI)) * Math.acos((Math.sin((-0.8333 - 0.0347 * (H / Math.abs(H)) * Math.sqrt(Math.abs(H))) * (Math.PI / 180)) - Math.sin(D * (Math.PI / 180)) * Math.sin(B * (Math.PI / 180))) / (Math.cos(D * (Math.PI / 180)) * Math.cos(B * (Math.PI/180))));
        var Vd = (180 / (15 * Math.PI)) * Math.acos((-Math.sin(Gd * (Math.PI / 180)) - Math.sin(D * (Math.PI/180)) * Math.sin(B * (Math.PI / 180))) / (Math.cos(D * (Math.PI/180)) * Math.cos(B * (Math.PI/180))));
        var Vn = (180 / (15 * Math.PI)) * Math.acos((-Math.sin(Gn * (Math.PI / 180)) - Math.sin(D * (Math.PI/180)) * Math.sin(B * (Math.PI / 180))) / (Math.cos(D * (Math.PI/180)) * Math.cos(B * (Math.PI/180))));
        var W =  (180 / (15 * Math.PI)) * Math.acos((Math.sin(Math.atan(1/(Sh+Math.tan(Math.abs(B - D)*Math.PI/180))))-Math.sin(D*Math.PI/180)*Math.sin(B*Math.PI/180))/(Math.cos(D*Math.PI/180)*Math.cos(B*Math.PI/180)));
        function FormatTime(x) {
            var hour =  Math.floor(x);
            var minute = Math.floor((x - hour) * 60);
            var second = Math.floor((((x - hour) * 60) - minute) * 60);
            return (hour + ':' + minute + ':' + second);
        }
        document.writeln('<pre>');
        document.writeln('Fajar   : ' + FormatTime(Z-Vd));
        document.writeln('Terbit  : ' + FormatTime(Z-U));
        document.writeln('Dhuhur  : ' + FormatTime(Z));
        document.writeln('Ashar   : ' + FormatTime(Z+W));
        document.writeln('Maghrib : ' + FormatTime(Z+U));
        document.writeln('Isya    : ' + FormatTime(Z+Vn));
        document.writeln('</pre>'); 
    // -->
    </script>
    

    Algoritma perhitungan waktu sholat di atas belum begitu akurat, bila ada yang punya algoritma lebih akurat bisa tambahkan comment.

    Share this post: | | | |
  • BigInteger, Tipe Data Baru di .NET 3.5

    Belum rampung mempelajari .NET 2.0, telah keluar .NET 3.0. Belum sempat pake .NET 3.0, sudah datang .NET 3.5. Teknologi M$ melaju sangat cepat, tak terasa sudah tertinggal jauh.

    Saat baca-baca MSDN mag (http://msdn.microsoft.com/msdnmag/issues/07/04/CLRInsideOut/#S5), saya tertarik dengan salah satu tulisan tentang tipe data baru yaitu BigInteger yang berada dalam namespace System.Numeric. Pada .NET Framework sebelumnya .NET 1.0 - 3.0, tipe data bilangan bulat terbesar adalah 64bit (System.Int64 dan System.UInt64). Berbeda dengan tipe data lainnya yang memiliki kapasitas tertentu (besar bit), BigInteger tidak memiliki kapasitas tertentu alias tidak terbatas (dibatasi oleh jumlah memori yang tersedia). Kita dapat membuat angka berapapun besarnya.

    BigInteger dapat digunakan untuk perhitungan dalam ilmu astronomi, fisika, matematika yang sebelumnya tidak bisa (sulit) dilakukan dengan .NET. Sebagai contoh untuk mengukur jarak bintang, biasanya menggunakan satuan tahun cahaya, bagaimana jika satuan tersebut dikonversi dengan meter atau kilometer. Sebuah angka yang sangat besar.

    Pada tulisan tersebut dicontohkan sebuah perhitungan faktorial 1000!. Tidak terfikir oleh saya sebelumnya bagaimana menghitung faktorial sampai sebesar itu. Dengan BigInteger, hal tersebut sangat mudah dilakukan.

    Share this post: | | | |
  • Linked List is My Favorite Data Structure

    Dari blog Pak Risman yang berjudul "What is your favorite data structure?", saya coba untuk menjawab pertanyaan "What is your favorite data structure?". Dari berbagai struktur data yang pernah saya pelajari, seranai berantai (linked list) adalah struktur data yang paling saya sukai.

    Seranai berantai menjadi favorit saya karena menjadi dasar struktur data pada pemprograman komputer. Berbagai struktur data yang lain dapat dibuat dengan seranai berantai, seperti larik (array), antrian (queue), tumpukan (stack), pohon (tree), dan lain sebagainya.

    Pada dasarnya, seranai berantai merupakan rangkaian beberapa simpul (node) dengan masing-masing simpul mengandung data dan referensi (taut [link]) ke simpul lainnya. Seranai berantai dapat digambarkan seperti gambar di bawah.

    Gambar 1. Seranai berantai

    Kelebihan seranai berantai dibandingkan dengan seranai (list) biasa adalah sangat dinamis dan efisien dalam pengalokasian sumber daya memory. Urutan Seranai berantai tidak harus sama dengan urutan data yang tersimpan di memory. Urutan simpul dapat diubah atau dibalik hanya dengan merubah referensinya saja, tanpa memindahkan lokasi objek tersebut dalam memory. Pengaksesan simpul seranai berantai tidak bisa dilakukan secara acak, simpul hanya bisa diakses dengan menelusuri jalur maju atau mundur. Penambahan dapat dilakukan dengan mengaitkan simpul baru ke dalam rantai. Penghapusan dapat dilakukan dengan memutus sambungan simpul dari rantai dan melepaskan alokasi memory untuk simpul tersebut. Beberapa jenis seranai berantai adalah seranai berantai tunggal (singly-linked list), seranai berantai ganda (doubly-linked list) dan seranai berantai melingkar (circularly-linked list).

    Seranai berantai dapat dibuat dengan berbagai macam bahasa pemprograman seperti Pascal, C, C++, C#, Java dan bahkan Assembler. Pada .NET Framework 2.0 terdapat class generic yang khusus menangani seranai berantai, yaitu LinkedList dan LinkedListNode. Kedua class tersebut terdapat dalam namespace System.Collections.Generic.

    Seranai Berantai Linier

    Seranai Berantai Tunggal

    Seranai berantai paling sederhana adalah seranai berantai tunggal (singly-linked list, atau disingkat slist). Pada seranai berantai ini, tiap simpul hanya memiliki satu taut (link) yang merujuk ke simpul berikutnya atau nilai null pada akhir simpul. Seranai berantai ini hanya bisa diakses maju dari awal simpul ke akhir simpul, seperti ditunjukkan pada Gambar 1.

    Pembuatan simpul

    Simpul dapat didefinisikan menggunakan struct (C), Record (Pascal) atau class. Dalam tulisan ini, saya akan paparkan bagaimana membuat seranai berantai menggunakan C#. Untuk membuat simpul kita dapat mendefinisikan objek LinkedListNode (.NET 2.0). Pada contoh berikut tidak digunakan objek LinkedListNode dengan maksud untuk memperjelas pemaparan. Berikut contoh deklarasi class simpul menggunakan C#.

    class Node
    {
        public int value;
        public Node next;
        public Node(int value, Node next)
        {
            this.value = value;
            this.next = next;
        }
    }

    Code 1. Class Node, simpul untuk seranai berantai tunggal

    Pada contoh di atas, class Node memiliki dua object value untuk data dan next yang mengacu pada class Node itu sendiri. Contoh berikit kita akan membuat tiga buah simpul dan kemudian dirangkai dalam satu rangkaian seranai berantai.

    Node node1 = new Node(1, null);  // buat node1
    Node node2 = new Node(2, node1); // buat node2 dan sambungkan ke node1
    Node node3 = new Node(3, node2); // buat node3 dan sambungkan ke node2

    Code 2. Pembuatan simpul seranai berantai

    Pembuatan simpul disini dilakukan dengan membuat instance class baru. Pertama dibuat node1 yang berisi nilai 1 dan taut berisi null. Simpul kedua ditambahkan dengan nilai 2 dan taut merujuk pada node1. Simpul berikutnya berisi nilai 3 dan taut merujuk pada node2. Kode di atas dapat diilustrasikan dengan gambar di bawah ini.

    Gambar 2. Seranai berantai tunggal

    Penambahan simpul

    Pada contoh sebelumnya, masing-masing simpul memiliki nama sendiri yang dapat diakses dengan nama objek (node1, node2, node3). Hal ini tidak dapat dilakukan untuk seranai berantai dinamis, karena tidak bisa memberikan nama pada setiap simpul. Mari lihat contoh berikut.

    Node node, newNode;          // deklarasi simpul node dan newNode
    node = new Node(1, null);    // buat simpul node dengan nilai 1
    newNode = new Node(2, null); // buat simpul baru (newNode) dengan nilai 2
    newNode.next = node;         // sambungkan simpul newNode ke simpul node
    node = newNode;              // pindahkan referensi objek node ke objek newNode
    newNode = new Node(3, null); // buat simpul baru (newNode) dengan nilai 2
    newNode.next = node;         // sambungkan simpul newNode ke simpul node
    node = newNode;              // pindahkan referensi objek node ke objek newNode

    Code 3. Penambahan simpul di awal seranai berantai

    Contoh di atas menghasilkan seranai berantai yang sama dengan contoh sebelumnya. Perbedaannya pada bagaimana cara menambahkan simpul baru pada seranai berantai. Di sini, dibutuhkan simpul bantu (newNode) untuk menambahkan simpul di awal seranai berantai. Dengan cara tersebut, berapapun panjang seranai berantai dapat ditambahkan.

    Langkah-langkah penambahan simpul di awal seranai berantai dilakukan sebagai berikut.

    1. Pada mulanya dibuat simpul (node) dengan nilai 1
    2. Selanjutnya dibuat simpul baru (newNode) dengan nilai 2
    3. Simpul baru disambungkan ke simpul sebelumnya (node)
    4. Referensi objek simpul awal (node) disamakan dengan simpul baru (newNode).
    5. Untuk menambahkan simpul lagi, ulangi langkah 2 sampai 4.

    Untuk lebih memahami bagaimana cara menambahkan simpul baru di awal seranai berantai, dapat dilihat ilustrasi berikut.

    // buat simpul node dengan nilai 1 
    node = new Node(1, null);
    // buat simpul baru (newNode) dengan nilai 2 
    newNode = new Node(2, null);
    // sambungkan simpul newNode ke simpul node
    newNode.next = node;
    // pindahkan referensi objek node ke objek newNode
    node = newNode;

    Ilustrasi 1. Penambahan simpul pada seranai berantai

    Untuk memudahkan penambahan simpul baru di awal seranai berantai dapat dibuat method seperti contoh kode berikut.

    static void insertBegining(int value, Node node)
    {
        Node newNode = new Node(value, null); // buat simpul baru (newNode)
        newNode.next = node;                  // sambungkan simpul newNode ke simpul node
        node = newNode;                       // jadikan newNode sebagai awal simpul
    }

    Code 4. Method insertBegining untuk menambahkan simpul pada awal seranai berantai

    Simpul ditambahkan pada seranai berantai dengan cara sebagai berikut.

    node = new Node(1, null);// buat simpul node dengan nilai 1
    insertBegining(2, node); // tambahkan simpul dengan nilai 2
    insertBegining(3, node); // tambahkan simpul dengan nilai 3

    Code 5. Contoh penggunaan method insertBegining

    Dari contoh di atas, seranai berantai yang dihasilkan dapat digambarkan pada gambar berikut.

    Gambar 3. Penambahan simpul pada awal seranai berantai

    Untuk menambahkan simpul di akhir seranai berantai dibutuhkan sebuah simpul bantu yang dinamakan simpul ekor (Tail). Simpul ekor ini yang digunakan sebagai acuan akhir dari seranai berantai.

    Langkah-langkah penambahan simpul di akhir seranai berantai dilakukan sebagai berikut.

    1. Pada mulanya dibuat simpul (node) dengan nilai 1 dan simpul ekor (tail), sambungkan tail.next ke node.
    2. Selanjutnya dibuat simpul baru (newNode) dengan nilai 2
    3. Simpul yang direferensikan oleh tail.next disambungkan ke simpul baru(newNode)
    4. Simpul ekor (tail) disambungkan ke simpul terakhir yang merupakan simpul baru (newNode).
    5. Untuk menambahkan simpul lagi, ulangi langkah 2 sampai 4.

    Untuk lebih memahami bagaimana cara menambahkan simpul baru di akhir seranai berantai, dapat dilihat ilustrasi berikut.

    // deklarasi simpul node dan tail
    Node node, tail;
     
    // buat simpul node dengan nilai 1
    node = new Node(1, null);
    // buat simpul tail dengan nilai 
    tail = new Node(-1, null);
    // sambungkan tail ke simpul node
    tail.next = node;
    
    // buat simpul baru (newNode) dengan nilai 2
    newNode = new Node(2, null);

    // sambungkan simpul terakir ke simpul baru (newNode) tail.next.next = newNode; // di sini simpul terakhir adalah simpul // yang ditaut oleh tail.next, sehingga // simpul_terakir.next (tail.next.next)
    //ditaut ke newNode

    // pindahkan taut simpul tail ke simpul terakir
    tail.next = newNode;

    Ilustrasi 1. Penambahan simpul di akhir seranai berantai

    Untuk memudahkan penambahan simpul baru di akhir seranai berantai dapat dibuat method seperti contoh kode berikut.

    static void insertEnding(int value, Node tail)
    {
        Node newNode = new Node(value, null); // buat simpul baru (newNode)
        tail.next.next = newNode;             // sambungkan simpul terakir ke simpul baru (newNode)
        tail.next = newNode;                  // pindahkan taut simpul tail ke simpul terakir
    }

    Code 4. Method insertBegining untuk menambahkan simpul pada akhir seranai berantai

    Pembacaan simpul

    Pembacaan simpul pada seranai berantai tunggal dilakukan secara berurutan dari awal sampai akhir. Pembacaan dilakukan dengan simpul bantu sebagai cursor yang menunjukkan posisi pembacaan saat ini.

    static void PrintNodes(Node node)
    {
        Node currentNode = node;
        while (currentNode != null)
        {
            Console.WriteLine(currentNode.value);
            currentNode = currentNode.next;
        }
    }

    Code 5. Pembacaan simpul

    Pada kode di atas, digunakan simpul bantu currentNode yang menunjukkan posisi pembacaan. Langhak-langkah pembacaan simpul sebagai berikut.

    1. Pada awalnya referensi objek currentNode menunjuk pada awal simpul (currentNode = node;)
    2. Dilakukan pembacaan nilai simpul (currentNode.value)
    3. Pindahkan currentNode ke simpul berikutnya (currentNode = currentNode.next;)
    4. Dilakukan perulangan dari langkah no 2 sampai currentNode.next bernilai null
    Penyisipan simpul di tengah seranai berantai

    Penyisipan simpul di tengah seranai berantai dibutuhkan cursor untuk menentukan posisi simpul yang akan disisipkan. Langkah-langkah penyisipan simpulsebagai berikut.

    1. Buat simpul baru (newNode)
    2. Sambungkan simpul baru ke depan simpul yang sedang ditunjuk oleh cursor (newNode.next = cursor.next.next)
    3. Sambungkan simpul yang ditunjuk oleh cursor dengan simpul baru (cursor.next.next = newNode)

     

    Untuk memudahkan penyisipan simpul baru dapat dibuat method seperti contoh kode berikut.

    static void insertNode(int value, ref Node cursor)
    {
        Node newNode = new Node(value, null); // buat simpul baru (newNode)
        if (cursor.next.next != null)         // jika cursor tidak di akhir simpul
        {
            newNode.next = cursor.next.next;  // Sambungkan simpul baru ke depan simpul yang sedang ditunjuk oleh cursor  
        }
        cursor.next.next = newNode;           // Sambungkan simpul yang ditunjuk oleh cursor dengan simpul baru 
    }

    Code 6. Penyisipan simpul

    Menghapus simpul

    Penghapusan simpul dilakukan dengan memutus rantai simpul kemudian menghapus simpul. Jika simpul berada di tengah rangkaian, rangkaian yang terputus perlu disambung kembali. Untuk memudahkan penghapusan simpul dibutuhkan dua cursor sebagai simpul bantu. Selain cursor juga dibutuhkan simpul head sebagai acuan awal simpul dalam rangkaian.

    Berikut langkah langkah untuk menghapus simpul dalam rangkaian.

    1. Buat cursor bantu yang ke dua (cursor2) yang menunjuk ke awal simpul.
    2. Jika cursor berada di awal rangkaian, kerjakan no 3 sampai 7, jika tidak kerjakan no 8
    3. Jika simpul yang ditunjuk cursor bukan simpul terakhir (hanya satu simpul) dalam rangkaian, kerjakan no 3 - 6, jika tidak kerjakan no 7
    4. Pindahkan cursor ke simpul berikutnya
    5. Putus sambungan awal simpul dengan simpul berikutnya.
    6. Pindahkan head ke simpul yang ditunjuk oleh cursor.
    7. Hapus rangkaian (karena hanya ada satu simpul dalam seranai berantai).
    8. Pindahkan cursor2 ke pisisi sebelum cursor.
    9. Jika cursor berada di akhir rangkaian, putuskan dari rangkaian sebelumnya (cursor2.next.next = null)
    10. Jika tidak berada di akhir rangkaian, pindahkan penunjukan simpul yang ditunjuk oleh cursor2 ke simpul setelah simpul yang ditunjuk oleh cursor (cursor2.next.next = cursor.next.next) dan putus sambungan simpul yang ditunjuk oleh cursor dengan simpul berikutnya (cursor.next.next = null)
    11. Pindahkan cursor ke simpul berikutnya (ditunjuk oleh cursor2).

    Untuk lebih memahami bagaimana cara menambahkan simpul baru di akhir seranai berantai, dapat dilihat ilustrasi berikut.

    Contoh seranai berantai pada gambar di samping, cursor berada di posisi simpul ke dua.
    // buat cursor2 dan sambungkan ke awal rangkaian
    Node cursor2 = new Node(-1, head.next);
    

    /* Pindahkan cursor2 ke posisi sebelum cursor */ while (cursor2.next.next != cursor.next) { cursor2.next = cursor2.next.next; } // pindahkan penunjukan simpul yang ditunjuk oleh cursor2 ke // simpul setelah simpul yang ditunjuk oleh cursor cursor2.next.next = cursor.next.next; // putus sambungan simpul yang ditunjuk oleh cursor dengan
    //
    simpul berikutnya cursor.next.next = null;

    // Pindahkan cursor ke simpul berikutnya
    cursor.next = cursor2.next;
    

    Ilustrasi 1. Penghapusan simpul pada seranai berantai

    Untuk memudahkan penghapusan simpul dapat dibuat method seperti contoh kode berikut.

    static void deleteNode(ref Node head, Node cursor)
    {
        Node cursor2 = new Node(-1, head.next);
        if (cursor.next == head.next)            // jika di awal rangkaian
        {
            if (cursor.next.next != null)        // jika cursor yang ditunjuk bukan akhir rangkaian
            {
                cursor.next = cursor.next.next;  // pindahkan cursor ke simpul berikutnya
                head.next.next = null;           // Putus sambungan awal simpul dengan simpul berikutnya.
                head.next = cursor.next;         // Pindahkan head ke simpul yang ditunjuk oleh cursor.
            }
            else
            {                                    
                cursor.next = null;              // hapus rangkaian, dengan set null pada cursor.next
                head.next = null;                // dan head.next
            }
        }
        else
        {
            if (cursor.next != null)
            {
                /* Pindahkan cursor2 ke posisi sebelum cursor */
                while (cursor2.next.next != cursor.next)
                {
                    cursor2.next = cursor2.next.next;
                }
                if (cursor.next.next == null)    // jika cursor di akhir rangkaian
                {
                    cursor2.next.next = null;    // putuskan dari rangkaian sebelumnya 
                }
                else {
                    // pindahkan penunjukan simpul yang ditunjuk oleh cursor2 ke 
                    // simpul setelah simpul yang ditunjuk oleh cursor 
                    cursor2.next.next = cursor.next.next;
                    // putus sambungan simpul yang ditunjuk oleh cursor dengan simpul berikutnya
                    cursor.next.next = null;
                }
                cursor.next = cursor2.next;      // Pindahkan cursor ke simpul berikutnya
            }
        }
    }

    Code 7. Method penghapusan simpul

    Secara keseluruhan baik dalam penambahan/penyisipan, pembacaan, dan penghapusan simpul dalam seranai berantai dibutuhkan beberapa simpul pembantu yaitu kepala (head), ekor (tail) dan cursor. Berikut contoh program secara keseluruhan berdasarkan beberapa contoh kode sebelumnya.

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    class Program
    {
        class Node
        {
            public int value;
            public Node next;
            public Node(int value, Node next)
            {
                this.value = value;
                this.next = next;
            }
        }
        static void insertEnding(int value, Node tail)
        {
            Node newNode = new Node(value, null); // buat simpul baru (newNode)
            tail.next.next = newNode;             // sambungkan simpul terakir ke simpul baru (newNode)
            tail.next = newNode;                  // pindahkan taut simpul tail ke simpul terakir
        }
        static void insertBegining(int value, Node head)
        {
            // buat simpul baru (newNode) dan sambungkan ke awal simpul
            Node newNode = new Node(value, head.next); 
            // set simpul kepala (head) ke simpul baru
            head.next = newNode;                  
        }
        static void insertNode(int value, Node cursor)
        {
            Node newNode = new Node(value, null); // buat simpul baru (newNode)
            if (cursor.next.next != null)         // jika cursor tidak di akhir simpul
            {
                newNode.next = cursor.next.next;  // Sambungkan simpul baru ke depan simpul yang sedang ditunjuk oleh cursor  
            }
            cursor.next.next = newNode;           // Sambungkan simpul yang ditunjuk oleh cursor dengan simpul baru 
        }
        static void deleteNode(Node head, Node cursor)
        {
            Node cursor2 = new Node(-1, head.next);
            if (cursor.next == head.next)            // jika di awal rangkaian
            {
                if (cursor.next.next != null)        // jika cursor yang ditunjuk bukan akhir rangkaian
                {
                    cursor.next = cursor.next.next;  // pindahkan cursor ke simpul berikutnya
                    head.next.next = null;           // Putus sambungan awal simpul dengan simpul berikutnya.
                    head.next = cursor.next;         // Pindahkan head ke simpul yang ditunjuk oleh cursor.
                }
                else
                {
                    cursor.next = null;              // hapus rangkaian, dengan set null pada cursor.next
                    head.next = null;                // dan head.next
                }
            }
            else
            {
                if (cursor.next != null)
                {
                    /* Pindahkan cursor2 ke posisi sebelum cursor */
                    while (cursor2.next.next != cursor.next)
                    {
                        cursor2.next = cursor2.next.next;
                    }
                    if (cursor.next.next == null)    // jika cursor di akhir rangkaian
                    {
                        cursor2.next.next = null;    // putuskan dari rangkaian sebelumnya 
                    }
                    else
                    {
                        // pindahkan penunjukan simpul yang ditunjuk oleh cursor2 ke 
                        // simpul setelah simpul yang ditunjuk oleh cursor 
                        cursor2.next.next = cursor.next.next;
                        // putus sambungan simpul yang ditunjuk oleh cursor dengan simpul berikutnya
                        cursor.next.next = null;
                    }
                    cursor.next = cursor2.next;      // Pindahkan cursor ke simpul berikutnya
                }
            }
        }
        static void PrintNodes(Node cursor)
        {
            while (cursor.next != null)
            {
                Console.Write(cursor.next.value + ", ");
                cursor.next = cursor.next.next;
            }
            Console.WriteLine("\n------------");
        }
        static void Main(string[] args)
        {
            Node head, tail, cursor;            // deklarasi simpul head, tail dan cursor
            head = new Node(-1, null);          // buat simpul head
            head.next = new Node(1, null);      // buat simpul pertama yang ditunjuk oleh head dengan nilai 1
            tail = new Node(-1, head.next);     // buat simpul tail menunjuk ke simpul pertama
            cursor = new Node(-1, head.next);   // buat simpul cursor menunjuk ke simpul pertama
    
            Console.WriteLine("Penambahan simpul di depan");
            insertBegining(2, head);            // tambahkan simpul dengan nilai 2 di awal rangkaian
            insertBegining(3, head);            // tambahkan simpul dengan nilai 3 di awal rangkaian
            /* Tampilkan dari awal rangkaian */
            cursor.next = head.next;
            PrintNodes(cursor);
            Console.WriteLine("Penambahan simpul di akhir");
            insertEnding(4, tail);              // tambahkan simpul dengan nilai 2
            insertEnding(5, tail);              // tambahkan simpul dengan nilai 3
            /* Tampilkan dari awal rangkaian */
            cursor.next = head.next;
            PrintNodes(cursor);
            Console.WriteLine("Penyisipan simpul di tengah (setelah simpul ke-2)");
            cursor.next = head.next.next;       // set cursor ke simpul ke-2
            insertNode(6, cursor);
            /* Tampilkan dari awal rangkaian */
            cursor.next = head.next;
            PrintNodes(cursor);
            Console.WriteLine("Penghapusan simpul ke-2");
            //cursor.next = cursor.next.next;
            cursor.next = head.next.next;       // set cursor ke simpul ke-2
            deleteNode(head, cursor);
            /* Tampilkan dari awal rangkaian */
            cursor.next = head.next;
            PrintNodes(cursor);
            Console.ReadKey();
        }
    }

    Code 8. Contoh program seranai berantai tunggal

    Seranai Berantai Ganda

    Seranai berantai ganda dapat diakses dalam dua arah, bisa maju dan mundur. Masing-masing simpul memiliki dua taut, satu taut merujuk ke simpul berikutnya (atau null pada akhir simpul) dan taut lainnya ke simpul sebelumnya (pada awal simpul). Penambahan, penyisipan dan penghapusan simpul tidak jauh beda dengan dengan seranai berantai tunggal.

     Gambar 4. Seranai berantai ganda

    Seranai Berantai Melingkar

    Seranai berantai melingkar memiliki struktur simpul yang sama seperti seranai berantai tunggal yaitu hanya memiliki satu penunjuk. Seranai berantai melingkar hanya dapat diakses maju. Perbedaannya, seranai berantai melingkar tidak memiliki simpul kepala dan ekor. Simpul akhir akan menunjuk ke simpul paling depan. Penambahan, penyisipan dan penghapusan simpul tidak jauh beda dengan dengan seranai berantai tunggal.

     Gambar 5. Seranai berantai melingkar

    Share this post: | | | |