Rumah > pembangunan bahagian belakang > Golang > Arrays vs Slices in Go: Memahami 'di bawah tudung' berfungsi secara visual

Arrays vs Slices in Go: Memahami 'di bawah tudung' berfungsi secara visual

Patricia Arquette
Lepaskan: 2024-12-21 18:27:15
asal
276 orang telah melayarinya

Arrays vs Slices in Go: Understanding the

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:

  1. Susun atur memori
  2. Mekanisme pertumbuhan
  3. Semantik rujukan
  4. Implikasi prestasi

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

Tatasusunan: Bekas Bersaiz Tetap ?

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.

Arrays vs Slices in Go: Understanding the

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Arrays vs Slices in Go: Understanding the

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Mengapa Array Disalin Secara Lalai?

Apabila anda menetapkan atau menghantar tatasusunan dalam Go, mereka membuat salinan secara lalai. Ini memastikan pengasingan data dan menghalang mutasi yang tidak dijangka.

Arrays vs Slices in Go: Understanding the

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

hirisan

Baiklah jadi anda tidak boleh melakukan var dynamic [size]int untuk menetapkan saiz dinamik, di sinilah slice berperanan.

Hirisan di bawah tudung

Keajaibannya terletak pada cara ia mengekalkan fleksibiliti ini sambil mengekalkan operasi dengan pantas.

Setiap kepingan dalam Go terdiri daripada tiga komponen kritikal:

Arrays vs Slices in Go: Understanding the

type slice struct {
    array unsafe.Pointer // Points to the actual data
    len   int           // Current number of elements
    cap   int           // Total available space
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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:

  1. Ia perlu menunjuk kepada memori mentah tanpa maklumat jenis
  2. Ia membolehkan Go melaksanakan kepingan untuk sebarang jenis T tanpa menghasilkan kod berasingan untuk setiap jenis.

Mekanisme dinamik hirisan

mari cuba bangunkan gerak hati untuk algoritma sebenar di bawah hud.

Arrays vs Slices in Go: Understanding the

Jika kita menggunakan intuisi kita boleh melakukan dua perkara:

  1. 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

  2. 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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

memandangkan menggandakan kepingan besar adalah pembaziran ingatan jadi apabila saiz kepingan bertambah, faktor pertumbuhan berkurangan.

Mari dapatkan pemahaman yang lebih baik dari perspektif penggunaan

Arrays vs Slices in Go: Understanding the

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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
Salin selepas log masuk
Salin selepas log masuk

Jom capai had

// New growth pattern
if capacity < 256 {
    capacity = capacity * 2
} else {
    capacity = capacity + capacity/4  // 25% growth
}
Salin selepas log masuk
Salin selepas log masuk

Aduh! Sekarang kita telah mencapai kapasiti kita, kita perlu berkembang. Inilah yang berlaku:

  1. Mengira kapasiti baharu (oldCap < 256, jadi dua kali ganda kepada 10)
  2. Peruntukkan tatasusunan sandaran baharu (katakan alamat memori baharu, 300)
  3. Menyalin elemen sedia ada ke tatasusunan sandaran baharu
  4. Menambah elemen baharu
  5. Kemas kini pengepala kepingan

Arrays vs Slices in Go: Understanding the

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Mengapa rujukan semantik?

  1. Prestasi: Menyalin struktur data yang besar adalah mahal
  2. Kecekapan ingatan: Mengelakkan pertindihan data yang tidak perlu
  3. Mendayakan paparan data yang dikongsi: Berbilang kepingan boleh merujuk tatasusunan sandaran yang sama
type slice struct {
    array unsafe.Pointer // Points to the actual data
    len   int           // Current number of elements
    cap   int           // Total available space
}
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

beginilah rupa pengepala hirisan:

// Old growth pattern
capacity = oldCapacity * 2  // Simple doubling
Salin selepas log masuk
Salin selepas log masuk

Corak penggunaan dan amaran untuk hirisan

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
}
Salin selepas log masuk
Salin selepas log masuk

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 selepas log masuk

Salin lwn Tambah

numbers = append(numbers, 10)
Salin selepas log masuk

Arrays vs Slices in Go: Understanding the

Mari kita akhiri ini dengan panduan pilihan yang jelas:

? Pilih Tatasusunan Bila:

  1. Anda tahu saiz yang tepat di hadapan
  2. Bekerja dengan data kecil dan tetap (seperti koordinat, nilai RGB)
  3. Prestasi adalah kritikal dan data sesuai pada tindanan
  4. Anda mahukan keselamatan taip dengan saiz

? Pilih Slices Bila:

  1. Saiz mungkin berubah
  2. Bekerja dengan data dinamik
  3. Memerlukan berbilang paparan data yang sama
  4. Memproses strim/koleksi

? 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!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan