Массивы в Golang

После чтения данного урока вы сможете:

  • Добавлять элементы в срезы массивов;
  • Разобраться, как устроены длина и вместимость массива.

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

Форум Гоферов

Мы работаем над форумом для программистов на Golang. Очень нужны модераторы которые хотят помочь с ответами для новичков и помочь в развитии Go-сообщества.

Go на Форум

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК Go в Telegram

Содержание статьи

Возможно, вам знакома ситуация, когда новые книги уже не помещаются на полку? Или кому-то в семьи приходиться делить комнату, потому что отдельной комнаты на каждого не хватает?

Как и у книжной полки, у массива есть определенная вместимость. Срез может сфокусироваться на части массива для книг, и потом расти до тех пор, пока не достигнет предела вместимости полки. Если полка заполняется, ее можно заменить полкой побольше или переместить книги в другое место. Затем поместить срез с книгами на новой полке большей вместимости.

Функция append в Go

У Международного астрономического союза (МАС) есть данные о пяти карликовых планетах в нашей Солнечной системе, но их может быть больше. Для добавления новых элементов к срезу dwarfs используйте встроенную функцию append, как показано в примере ниже:

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

Срез dwarfs стартует как вид внутрь массива из пяти элементов, но предыдущий код добавляет к нему еще четыре элемента. Как такое возможно? Чтобы разобраться, сперва разберем такое понятие, как вместимость массива, а также встроенную функцию cap.

Вопрос для проверки: 

Сколько карликовых планет значится в Листинге 1? Какую функцию можно использовать для определения точного числа?

Длина и вместимость среза в Golang

Число элементов, что видны через срез, определяется его длиной. Если у среза есть базовый массив, что большего размера, у среза остается вместимость для роста.

В следующем примере объявляется функция для вывода длины и вместимости среза:

У среза, созданного через dwarfs[1:2], длина равна 1, и вместимость для 4 элементов.

Вопрос для проверки:

Почему вместимость среза dwarfs[1:2] равна 4?

Разбор функции append в Go

Через использование функции dump из Листинга 2 в следующем примере показано, как функция append влияет на вместимость.

В массиве, поддерживающем dwarfs1, недостаточно места (вместимости) для добавления элемента "Оркус", поэтому append копирует содержимое dwarfs1 к новому перемещенному массиву с двойной вместимостью. Это показано на Схеме 1. Срез dwarfs2 указывает на новый перемещенный массив. Добавленная вместимость нужна для создания места, что потребуется для следующей функции append.

срезы массива go

Схема 1: Функция append перемещает новый массив, увеличивая его вместимость в случае необходимости.

Показать что, dwarfs2 и dwarfs3 ссылаются на иной массив, нежели dwarfs1, можно через изменение какого-то элемента и последующего вывода указанных трех срезов.

Задание для проверки:

При модификации элемента среза dwarfs3 из Листинга 3 изменятся ли срезы dwarfs2 и dwarfs1?

Трех-индексный срез в Golang

С появлением версии Go 1.2 был введен трех-индексный срез, что нужен для ограничения вместимости итогового среза. В следующем примере у terrestrial длина и вместимость 4. Добавление элемента "Церера" приводит к перемещению нового массива, оставляя массив planets не измененным.

Если третий индекс не уточняется, вместимость terrestrial равна 8. Добавление элемента "Церера" не перемещает новый массив, а вместо этого переписывает "Юпитер":

Только если вы не хотите переписать элемент "Юпитер", нужно, чтобы при каждом срезе значился по умолчанию трех-индексный срез.

Вопрос для проверки:

Когда нужно использовать трех-индексный срез?

Предварительное выделение срезов через make в Go

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

Функция make в следующем примере уточняет длину (0) и вместимость (10) среза dwarfs. Перед заполнением dwarfs можно добавить до 10 элементов, после чего append должен будет выделить новый массив.

Аргумент вместимости опционален. Начиная с длины и вместимости 10, вы можете использовать make([]string, 10). Каждый из 10 элементов содержит нулевое значение собственного типа, в данном случае это пустая строка. Встроенная функция append позволит добавить 11-й элемент.

Вопрос для проверки:

В чем преимущество создания среза через make?

Объявление вариативных функций в Golang

Printf и append являются вариативными функциями, так как они принимают переменное число аргументов. Для объявления вариативной функции используется многоточие (...) с последним параметром, как показано в примере ниже:

Параметр worlds является срезом строк, что содержит ноль или более аргументов, передаваемых в terraform:

Для передачи среза вместо множества аргументов, расширьте срез через многоточие:

Если бы terraform модифицировала элементы параметра worlds, срез planets также зафиксировал бы эти изменения. Через использование newWorlds функция terraform избегает изменения и передачи аргументов.

Вопрос для проверки:

Назовите три случая использования многоточия (...) в Go?

Заключение

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

Итоговое задание для проверки: 

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