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:
var b strings.Builder
b.WriteString("Halo")
fmt.Println(b.String())Output yang Dihasilkan:
HaloSelain 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:
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:])
}go run builder-demo.goOutput yang Dihasilkan:
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-999Mari kita telaah baris demi baris program di atas:
- Baris 11-17 (blok dasar): empat potongan ditulis berurutan ke Builder
b, hasil akhirnyaHalo, Budi!diambil sekali lewatString(), danLen()melaporkan 11 byte tanpa membangun string baru. - Baris 20-29 (blok Grow): kapasitas 16 byte dipesan di muka; loop menulis empat nama bahasa dengan pemisah
", "hanya pada iterasi kedua dan setelahnya, menghasilkango, rust, py, js. - Baris 32-34 (Reset): isi Builder
daftardikosongkan lalu diisi ulang menjadimulai lagi— variabel yang sama dipakai dua tugas. - Baris 37-44 (loop 1000 potongan): loop
for i := range 1000(bentuk Go 1.22) menulispotongan-, angka hasilstrconv.Itoa, dan pemisah;; seluruhnya menempel di satu buffer. - Baris 45-48 (verifikasi hasil):
String()dipanggil sekali di luar loop;lenmenghitung 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 #
- Dokumentasi package strings — bagian type Builder beserta seluruh method-nya.
- Blog Go: Strings, bytes, runes and characters — latar belakang model string yang dioptimasi Builder.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
builder-demo.go
Unduh berkaspackage 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:])
}