Pernahkah anda mencuba mengemas untuk perjalanan tanpa mengetahui berapa lama anda akan berada di sana? Itulah yang berlaku apabila kami menyimpan data dalam Go. Kadang-kadang, seperti ketika mengemas untuk perjalanan hujung minggu, kita tahu dengan tepat berapa banyak perkara yang perlu kita simpan; masa lain, seperti, apabila mengemas untuk perjalanan di mana kita berkata, "Saya akan kembali apabila saya sudah bersedia," kita tidak melakukannya.
Mari kita mendalami dunia tatasusunan Go dan potong dalaman melalui ilustrasi ringkas. Kami akan meneliti:
Menjelang akhir bacaan ini, anda akan dapat memahami masa untuk menggunakan tatasusunan berbanding masa untuk menggunakan kepingan dengan bantuan contoh dunia sebenar dan gambar rajah ingatan
Fikirkan tatasusunan sebagai satu blok memori di mana setiap elemen terletak bersebelahan antara satu sama lain, seperti deretan kotak yang disusun dengan sempurna.
Apabila anda mengisytiharkan nombor var [5]int, Go menyimpan memori bersebelahan yang cukup tepat untuk memegang 5 integer, tidak lebih, tidak kurang.
Memandangkan memori tetap bersebelahan, ia tidak boleh saiz semasa masa jalan.
func main() { // Zero-value initialization var nums [3]int // Creates [0,0,0] // Fixed size nums[4] = 1 // Runtime panic: index out of range // Sized during compilation size := 5 var dynamic [size]int // Won't compile: non-constant array bound }
Saiz adalah sebahagian daripada jenis tatasusunan. Ini bermakna [5]int dan [6]int adalah jenis yang sama sekali berbeza, sama seperti int dan rentetan adalah berbeza.
func main() { // Different types! var a [5]int var b [6]int // This won't compile a = b // compile error: cannot use b (type [6]int) as type [5]int // But this works var c [5]int a = c // Same types, allowed }
Apabila anda menetapkan atau menghantar tatasusunan dalam Go, mereka membuat salinan secara lalai. Ini memastikan pengasingan data dan menghalang mutasi yang tidak dijangka.
func modifyArrayCopy(arr [5]int) { arr[0] = 999 // Modifies the copy, not original } func modifyArray(arr *[5]int){ arr[0] = 999 // Modifies the original, since reference is passed } func main() { numbers := [5]int{1, 2, 3, 4, 5} modifyArrayCopy(numbers) fmt.Println(numbers[0]) // prints 1, not 999 modifyArray(&numbers) fmt.Println(numbers[0]) // prints 999 }
Baiklah jadi anda tidak boleh melakukan var dynamic [size]int untuk menetapkan saiz dinamik, di sinilah slice berperanan.
Keajaibannya terletak pada cara ia mengekalkan fleksibiliti ini sambil mengekalkan operasi dengan pantas.
Setiap kepingan dalam Go terdiri daripada tiga komponen kritikal:
type slice struct { array unsafe.Pointer // Points to the actual data len int // Current number of elements cap int // Total available space }
Apa yang tidak selamat.Penunjuk??
Penunjuk yang tidak selamat ialah cara Go mengendalikan alamat memori mentah tanpa kekangan keselamatan jenis. Ia "tidak selamat" kerana ia memintas sistem jenis Go, membenarkan manipulasi memori langsung.
Fikirkan ia sebagai bersamaan Go dengan penunjuk kosong C.
Apakah tatasusunan itu?
Apabila anda membuat kepingan, Go memperuntukkan blok memori bersebelahan dalam timbunan (tidak seperti tatasusunan) yang dipanggil tatasusunan sandaran. Kini tatasusunan dalam struct slice menghala ke permulaan blok memori itu.
Medan tatasusunan menggunakan tidak selamat.Penunjuk kerana:
mari cuba bangunkan gerak hati untuk algoritma sebenar di bawah hud.
Jika kita menggunakan intuisi kita boleh melakukan dua perkara:
Kami boleh mengetepikan ruang yang begitu besar dan boleh menggunakannya apabila diperlukan
kebaikan: Mengendalikan keperluan yang semakin meningkat sehingga satu tahap
keburukan: Pembaziran memori, boleh dikatakan boleh mencapai had
Kami boleh menetapkan saiz rawak pada mulanya dan apabila elemen dilampirkan, kami boleh mengagihkan semula memori pada setiap tambahan
kebaikan: Mengendalikan kes sebelumnya, boleh berkembang mengikut keperluan
keburukan: peruntukan semula adalah mahal dan pada setiap tambahan ia akan menjadi lebih teruk
Kita tidak boleh mengelak pengagihan semula kerana apabila kapasiti mencecah seseorang perlu berkembang. Kita boleh meminimumkan peruntukan semula supaya kos sisipan/tambahan berikutnya adalah malar (O(1)). Ini dipanggil kos terlunas.
Bagaimana kita boleh melakukannya?
versi till Go v1.17 formula berikut sedang digunakan:
func main() { // Zero-value initialization var nums [3]int // Creates [0,0,0] // Fixed size nums[4] = 1 // Runtime panic: index out of range // Sized during compilation size := 5 var dynamic [size]int // Won't compile: non-constant array bound }
dari versi Go v1.18:
func main() { // Different types! var a [5]int var b [6]int // This won't compile a = b // compile error: cannot use b (type [6]int) as type [5]int // But this works var c [5]int a = c // Same types, allowed }
memandangkan menggandakan kepingan besar adalah pembaziran ingatan jadi apabila saiz kepingan bertambah, faktor pertumbuhan berkurangan.
func modifyArrayCopy(arr [5]int) { arr[0] = 999 // Modifies the copy, not original } func modifyArray(arr *[5]int){ arr[0] = 999 // Modifies the original, since reference is passed } func main() { numbers := [5]int{1, 2, 3, 4, 5} modifyArrayCopy(numbers) fmt.Println(numbers[0]) // prints 1, not 999 modifyArray(&numbers) fmt.Println(numbers[0]) // prints 999 }
mari tambah beberapa elemen pada kepingan kami
type slice struct { array unsafe.Pointer // Points to the actual data len int // Current number of elements cap int // Total available space }
Memandangkan kami mempunyai kapasiti (5) > panjang (3), Pergi:
Menggunakan tatasusunan sokongan sedia ada
Tempat 10 pada indeks 3
Menambah panjang sebanyak 1
// Old growth pattern capacity = oldCapacity * 2 // Simple doubling
Jom capai had
// New growth pattern if capacity < 256 { capacity = capacity * 2 } else { capacity = capacity + capacity/4 // 25% growth }
Aduh! Sekarang kita telah mencapai kapasiti kita, kita perlu berkembang. Inilah yang berlaku:
func main() { // Zero-value initialization var nums [3]int // Creates [0,0,0] // Fixed size nums[4] = 1 // Runtime panic: index out of range // Sized during compilation size := 5 var dynamic [size]int // Won't compile: non-constant array bound }
apa yang berlaku jika hirisan besar?
func main() { // Different types! var a [5]int var b [6]int // This won't compile a = b // compile error: cannot use b (type [6]int) as type [5]int // But this works var c [5]int a = c // Same types, allowed }
Memandangkan kapasiti ialah 256, Go menggunakan formula pertumbuhan selepas 1.18:
Kapasiti baharu = OldCap oldCap/4
256 256/4 = 256 64 = 320
func modifyArrayCopy(arr [5]int) { arr[0] = 999 // Modifies the copy, not original } func modifyArray(arr *[5]int){ arr[0] = 999 // Modifies the original, since reference is passed } func main() { numbers := [5]int{1, 2, 3, 4, 5} modifyArrayCopy(numbers) fmt.Println(numbers[0]) // prints 1, not 999 modifyArray(&numbers) fmt.Println(numbers[0]) // prints 999 }
type slice struct { array unsafe.Pointer // Points to the actual data len int // Current number of elements cap int // Total available space }
beginilah rupa pengepala hirisan:
// Old growth pattern capacity = oldCapacity * 2 // Simple doubling
Kemas kini secara tidak sengaja
memandangkan slice menggunakan semantik rujukan, ia tidak menghasilkan salinan yang mungkin membawa kepada mutasi tidak sengaja kepada kepingan asal jika tidak berhati-hati.
// New growth pattern if capacity < 256 { capacity = capacity * 2 } else { capacity = capacity + capacity/4 // 25% growth }
Operasi tambah mahal
numbers := make([]int, 3, 5) // length=3 capacity // Memory Layout after creation: Slice Header: { array: 0xc0000b2000 // Example memory address len: 3 cap: 5 } Backing Array at 0xc0000b2000: [0|0|0|unused|unused]
Salin lwn Tambah
numbers = append(numbers, 10)
Mari kita akhiri ini dengan panduan pilihan yang jelas:
? Pilih Tatasusunan Bila:
? Pilih Slices Bila:
? Lihat projek notion-to-md! Ia adalah alat yang menukar halaman Notion kepada Markdown, sesuai untuk pencipta kandungan dan pembangun. Sertai komuniti perselisihan kami.
Atas ialah kandungan terperinci Arrays vs Slices in Go: Memahami 'di bawah tudung' berfungsi secara visual. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!