Массивы и слайсы

Что такое массив в Go?

В языке программирования Go массив — это структура данных, которая представляет собой фиксированную последовательность элементов одного типа. Размер массива задается при его создании и не может изменяться в дальнейшем. Элементы массива индексируются, начиная с нуля.

Пример объявления и использования массива в Go:

package main

import "fmt"

func main() {
    // Объявление массива из 5 целых чисел
    var numbers [5]int

    // Инициализация элементов массива
    numbers[0] = 10
    numbers[1] = 20
    numbers[2] = 30
    numbers[3] = 40
    numbers[4] = 50

    // Вывод элементов массива
    for i := 0; i < len(numbers); i++ {
        fmt.Println(numbers[i])
    }
}

В этом примере создается массив numbers из 5 целых чисел, инициализируются его элементы и выводятся на экран.

Что такое слайс в Go?

Слайс в Go — это структура данных, которая представляет собой динамический массив. В отличие от массивов, слайсы могут изменять свой размер во время выполнения программы. Слайсы являются более гибкими и удобными для работы с последовательностями данных.

Слайс состоит из трех компонентов:

  1. Указатель на массив, который хранит элементы.

  2. Длина (количество элементов в слайсе).

  3. Емкость (максимальное количество элементов, которые могут быть размещены в слайсе без выделения новой памяти).

Пример объявления и использования слайса в Go:

package main

import "fmt"

func main() {
    // Объявление и инициализация слайса
    numbers := []int{10, 20, 30, 40, 50}

    // Добавление элементов в слайс
    numbers = append(numbers, 60, 70)

    // Вывод элементов слайса
    for i := 0; i < len(numbers); i++ {
        fmt.Println(numbers[i])
    }

    // Вывод длины и емкости слайса
    fmt.Printf("Length: %d, Capacity: %d\n", len(numbers), cap(numbers))
}

В этом примере создается слайс numbers, инициализируется несколькими значениями, затем в него добавляются новые элементы с помощью функции append. После этого элементы слайса выводятся на экран, а также выводятся его длина и емкость.

Чем слайс отличается от массива?

В отличие от массива, который представляет фиксированную по размеру коллекцию данных, слайс в GoLang является более гибкой структурой, позволяющей изменять свою длину. Слайс создает массив, который может изменять свой размер, в то время как массив имеет фиксированную длину. Поэтому, слайс в GoLang является надмножеством массивов, обеспечивая большую гибкость при работе с данными.

Как сконвертировать массив в слайс в golang?

Для конвертации массива в слайс в Golang можно использовать следующий подход:

array := [5]int{1, 2, 3, 4, 5}
slice := array[:]

Здесь:

  • array - массив из 5 целых чисел

  • slice := array[:] создает слайс, который ссылается на тот же массив данных, что и array[1][4]

Другой способ создать слайс из массива - явно указать длину и емкость:

array := [5]int{1, 2, 3, 4, 5}
slice := array[:3:4]

Это создаст слайс длиной 3 и емкостью 4, ссылающийся на первые 3 элемента массива array[1].

Важно понимать, что слайс не копирует данные массива, а создает ссылку на те же данные. Изменение элементов слайса повлияет на соответствующие элементы исходного массива[4].

Таким образом, для конвертации массива в слайс в Golang достаточно создать слайс, используя оператор среза [:] и указав массив в качестве операнда слева.

Citations: [1] https://golangify.com/convert-types [2] https://folko.gitbook.io/goland/voprosy-sobesedovaniya/bazovye-voprosy-po-golang [3] https://habr.com/ru/companies/vk/articles/574542/ [4] https://golangify.com/string [5] https://www.nic.ru/help/samye-rasprostranennye-tipy-dannyh-v-go-v-chem-raznica_11652.html

Важные моменты

  • Срезы в Go являются ссылочными типами, что означает, что они указывают на оригинальный массив. Изменения в срезе отражаются на массиве и наоборот.

  • Если вы изменяете размер среза так, что он выходит за пределы исходного массива, Go автоматически выделит новый участок памяти для размещения данных среза. Это важно для понимания производительности и управления памятью.

В каком случае меняется размер слайса Go?

В Go слайсы являются динамическими массивами, которые могут изменять свой размер. Размер слайса может изменяться в следующих случаях:

1. При использовании функции append

Функция append добавляет элементы в слайс и может изменять его размер. Если емкость (capacity) слайса недостаточна для добавления новых элементов, создается новый массив с увеличенной емкостью, и элементы копируются в него.

Пример:

package main

import "fmt"

func main() {
    s := []int{1, 2, 3}
    fmt.Println("Before append:", s, "len:", len(s), "cap:", cap(s))

    s = append(s, 4, 5)
    fmt.Println("After append:", s, "len:", len(s), "cap:", cap(s))
}

Вывод:

Before append: [1 2 3] len: 3 cap: 3
After append: [1 2 3 4 5] len: 5 cap: 6

2. При срезе (slicing)

Создание нового слайса путем среза существующего слайса может изменить его размер, но не емкость. Новый слайс будет ссылаться на тот же массив, что и исходный слайс.

Пример:

package main

import "fmt"

func main() {
    s := []int{1, 2, 3, 4, 5}
    fmt.Println("Original slice:", s, "len:", len(s), "cap:", cap(s))

    s2 := s[1:4]
    fmt.Println("Sliced slice:", s2, "len:", len(s2), "cap:", cap(s2))
}

Вывод:

Original slice: [1 2 3 4 5] len: 5 cap: 5
Sliced slice: [2 3 4] len: 3 cap: 4

3. При использовании функции copy

Функция copy копирует элементы из одного слайса в другой. Если целевой слайс имеет меньший размер, чем исходный, копируются только те элементы, которые помещаются в целевой слайс.

Пример:

package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, 3)
    fmt.Println("Before copy:", dst, "len:", len(dst), "cap:", cap(dst))

    copy(dst, src)
    fmt.Println("After copy:", dst, "len:", len(dst), "cap:", cap(dst))
}

Вывод:

Before copy: [0 0 0] len: 3 cap: 3
After copy: [1 2 3] len: 3 cap: 3

4. При использовании функции append с срезом

Вы можете использовать append для добавления элементов из одного слайса в другой. Это также может изменить размер целевого слайса.

Пример:

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3}
    s2 := []int{4, 5, 6}
    fmt.Println("Before append:", s1, "len:", len(s1), "cap:", cap(s1))

    s1 = append(s1, s2...)
    fmt.Println("After append:", s1, "len:", len(s1), "cap:", cap(s1))
}

Вывод:

Before append: [1 2 3] len: 3 cap: 3
After append: [1 2 3 4 5 6] len: 6 cap: 6

Заключение

Размер слайса в Go может изменяться при использовании функции append, при создании нового слайса путем среза, при копировании элементов с помощью функции copy, а также при добавлении элементов из одного слайса в другой с помощью append. Эти операции позволяют гибко управлять размером и содержимым слайсов, что делает их мощным инструментом для работы с динамическими массивами в Go.

В каких случаях append создает копию слайса?

Функция append в Go может создавать копию слайса в определенных случаях, связанных с емкостью (capacity) исходного слайса. Понимание этих случаев важно для эффективного управления памятью и производительности.

Когда append создает копию слайса

  1. Недостаточная емкость (capacity):

    • Если емкость исходного слайса недостаточна для размещения новых элементов, append создает новый массив с увеличенной емкостью и копирует в него элементы исходного слайса. Новый массив затем используется для хранения добавленных элементов.

Пример:

package main

import "fmt"

func main() {
    s := make([]int, 3, 3) // len=3, cap=3
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Println("Before append:", s, "len:", len(s), "cap:", cap(s))

    s = append(s, 4) // Недостаточная емкость, создается новый массив
    fmt.Println("After append:", s, "len:", len(s), "cap:", cap(s))
}

Вывод:

Before append: [1 2 3] len: 3 cap: 3
After append: [1 2 3 4] len: 4 cap: 6

Когда append не создает копию слайса

  1. Достаточная емкость (capacity):

    • Если емкость исходного слайса достаточна для размещения новых элементов, append просто добавляет элементы в существующий массив, не создавая новый массив.

Пример:

package main

import "fmt"

func main() {
    s := make([]int, 3, 5) // len=3, cap=5
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Println("Before append:", s, "len:", len(s), "cap:", cap(s))

    s = append(s, 4) // Достаточная емкость, новый массив не создается
    fmt.Println("After append:", s, "len:", len(s), "cap:", cap(s))
}

Вывод:

Before append: [1 2 3] len: 3 cap: 5
After append: [1 2 3 4] len: 4 cap: 5

Пример с проверкой изменения адреса массива

Для наглядности можно проверить, изменился ли адрес массива после вызова append. Если адрес изменился, значит, был создан новый массив.

Пример:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    s := make([]int, 3, 3) // len=3, cap=3
    s[0], s[1], s[2] = 1, 2, 3
    fmt.Printf("Before append: %v, len: %d, cap: %d, address: %p\n", s, len(s), cap(s), unsafe.Pointer(&s[0]))

    s = append(s, 4) // Недостаточная емкость, создается новый массив
    fmt.Printf("After append: %v, len: %d, cap: %d, address: %p\n", s, len(s), cap(s), unsafe.Pointer(&s[0]))
}

Вывод:

Before append: [1 2 3], len: 3, cap: 3, address: 0xc0000140a0
After append: [1 2 3 4], len: 4, cap: 6, address: 0xc0000140c0

Заключение

Функция append в Go создает копию слайса, когда емкость исходного слайса недостаточна для размещения новых элементов. В этом случае создается новый массив с увеличенной емкостью, и элементы исходного слайса копируются в него. Если емкость исходного слайса достаточна, append просто добавляет элементы в существующий массив, не создавая новый массив. Понимание этих механизмов помогает эффективно управлять памятью и производительностью при работе со слайсами в Go.

Last updated