Membangun teks dari banyak potongan adalah pekerjaan rutin: menyusun laporan dari ratusan baris, merangkai URL dari parameter, atau membuat pesan log dari banyak field. Refleks pertama pemula biasanya operator +=, padahal di balik kesederhanaannya operator tersebut menyalin data berulang kali. Package strings menyediakan strings.Builder khusus untuk tugas ini: sebuah penampung yang mengakumulasi potongan di satu buffer internal lalu menghasilkan string final tepat satu kali. Pada chapter ini kita akan menghitung biaya +=, memakai Builder dari kasus paling sederhana hingga gabung ribuan potongan, lalu mengenal batasan penyalinannya.

Mengapa += Boros #

Chapter String yang Immutable sudah menunjukkan bahwa setiap penggabungan menghasilkan string baru. Konsekuensinya menumpuk di dalam loop: penggabungan ke-k menyalin seluruh isi hasil sebelumnya plus potongan baru. Angka sederhana memperjelasnya — menggabungkan 10 potongan masing-masing 10 byte dengan += menyalin 10 + 20 + 30 + … + 100 = 550 byte, padahal hasil akhirnya hanya 100 byte. Potongan yang jumlahnya ribuan membuat rasio ini makin buruk karena total penyalinan tumbuh kuadratik.

strings.Builder memutus rantai salinan itu: seluruh WriteString menempelkan potongan ke satu buffer []byte internal yang tumbuh secara amortisasi (seperti append), dan pemanggilan String() di akhir menghasilkan string final tanpa penyalinan ulang isi buffer.

Penggunaan Dasar #

Builder siap pakai langsung dari nilai nolnya — tanpa konstruktor. Tulis potongan dengan WriteString, lalu ambil hasilnya dengan String:

go
var b strings.Builder
b.WriteString("Halo")
fmt.Println(b.String())

Output yang Dihasilkan:

text
Halo

Selain WriteString, tersedia WriteByte untuk satu byte, WriteRune untuk satu karakter Unicode, dan Write untuk potongan []byte mentah — keluarga lengkapnya dijelaskan dokumentasi strings. Fungsi Len melaporkan jumlah byte yang sudah tertampung tanpa membangun string final. Nilai balik String() tetap valid selama Builder tidak ditulis lagi; hasilnya adalah string biasa yang bisa disimpan di mana saja.

Grow dan Reset #

Dua method pelengkap mengoptimalkan siklus hidup Builder. Grow(n) memesan kapasitas minimal n byte di muka, sehingga penulisan beruntun tidak memicu realokasi di tengah jalan — nilainya cukup perkiraan kasar, bukan angka harus tepat. Reset() mengosongkan isi sekaligus kapasitasnya agar satu variabel Builder bisa dipakai ulang untuk tugas berikutnya tanpa alokasi variabel baru.

Builder sebagai io.Writer #

strings.Builder mengimplementasikan interface io.Writer, sehingga seluruh alat tulis standard library bisa mengisinya secara langsung. Pemanggilan fmt.Fprintf(&besar, "potongan-%d", i) sah dan praktis untuk format campuran. Program utuh di bawah memilih jalur lain — WriteString plus strconv.Itoa — untuk menunjukkan bahwa penulisan manual pun sama mudahnya, sekaligus mengulang pelajaran chapter Package strconv .

Program Utuh #

Berkas builder-demo.go membuktikan seluruh fitur sekaligus menggabungkan 1000 potongan:

go
package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// Dasar: buat Builder, tulis, lalu ambil hasilnya.
	var b strings.Builder
	b.WriteString("Halo")
	b.WriteString(", ")
	b.WriteString("Budi")
	b.WriteString("!")
	fmt.Println("hasil   :", b.String())
	fmt.Println("panjang:", b.Len())

	// Grow menyiapkan kapasitas sebelum penulisan beruntun.
	bahasa := []string{"go", "rust", "py", "js"}
	var daftar strings.Builder
	daftar.Grow(16)
	for i, p := range bahasa {
		if i > 0 {
			daftar.WriteString(", ")
		}
		daftar.WriteString(p)
	}
	fmt.Println("daftar  :", daftar.String())

	// Reset mengosongkan isi agar Builder dipakai ulang.
	daftar.Reset()
	daftar.WriteString("mulai lagi")
	fmt.Println("reset   :", daftar.String())

	// Menggabung ribuan potongan menjadi satu string.
	var besar strings.Builder
	for i := range 1000 {
		if i > 0 {
			besar.WriteString(";")
		}
		besar.WriteString("potongan-")
		besar.WriteString(strconv.Itoa(i))
	}
	hasil := besar.String()
	fmt.Println("jumlah  :", len(hasil), "byte")
	fmt.Println("awal    :", hasil[:23])
	fmt.Println("akhir   :", hasil[len(hasil)-18:])
}
bash
go run builder-demo.go

Output yang Dihasilkan:

text
hasil   : Halo, Budi!
panjang: 11
daftar  : go, rust, py, js
reset   : mulai lagi
jumlah  : 12889 byte
awal    : potongan-0;potongan-1;p
akhir   : n-998;potongan-999

Mari kita telaah baris demi baris program di atas:

  1. Baris 11-17 (blok dasar): empat potongan ditulis berurutan ke Builder b, hasil akhirnya Halo, Budi! diambil sekali lewat String(), dan Len() melaporkan 11 byte tanpa membangun string baru.
  2. Baris 20-29 (blok Grow): kapasitas 16 byte dipesan di muka; loop menulis empat nama bahasa dengan pemisah ", " hanya pada iterasi kedua dan setelahnya, menghasilkan go, rust, py, js.
  3. Baris 32-34 (Reset): isi Builder daftar dikosongkan lalu diisi ulang menjadi mulai lagi — variabel yang sama dipakai dua tugas.
  4. Baris 37-44 (loop 1000 potongan): loop for i := range 1000 (bentuk Go 1.22) menulis potongan-, angka hasil strconv.Itoa, dan pemisah ;; seluruhnya menempel di satu buffer.
  5. Baris 45-48 (verifikasi hasil): String() dipanggil sekali di luar loop; len menghitung 12889 byte, sedangkan irisan awal dan akir memperlihatkan potongan pertama dan terakhir tanpa mencetak 12 ribu byte penuh ke layar.

Kerangka penulisan efisien kini lengkap: kita bisa membaca byte, mengonversi angka, dan merakit teks besar. Selanjutnya teks itu perlu diurai kembali — memecah baris CSV, memisahkan kunci dan nilai konfigurasi — tugas-tugas yang menanti di chapter Parsing Text .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

builder-demo.go

Unduh berkas
go
package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// Dasar: buat Builder, tulis, lalu ambil hasilnya.
	var b strings.Builder
	b.WriteString("Halo")
	b.WriteString(", ")
	b.WriteString("Budi")
	b.WriteString("!")
	fmt.Println("hasil   :", b.String())
	fmt.Println("panjang:", b.Len())

	// Grow menyiapkan kapasitas sebelum penulisan beruntun.
	bahasa := []string{"go", "rust", "py", "js"}
	var daftar strings.Builder
	daftar.Grow(16)
	for i, p := range bahasa {
		if i > 0 {
			daftar.WriteString(", ")
		}
		daftar.WriteString(p)
	}
	fmt.Println("daftar  :", daftar.String())

	// Reset mengosongkan isi agar Builder dipakai ulang.
	daftar.Reset()
	daftar.WriteString("mulai lagi")
	fmt.Println("reset   :", daftar.String())

	// Menggabung ribuan potongan menjadi satu string.
	var besar strings.Builder
	for i := range 1000 {
		if i > 0 {
			besar.WriteString(";")
		}
		besar.WriteString("potongan-")
		besar.WriteString(strconv.Itoa(i))
	}
	hasil := besar.String()
	fmt.Println("jumlah  :", len(hasil), "byte")
	fmt.Println("awal    :", hasil[:23])
	fmt.Println("akhir   :", hasil[len(hasil)-18:])
}