Работа с массивами в Go

В Go массив – это упорядоченная последовательность элементов, емкость которой определяется во время ее создания. Как только массиву был выделен заданный размер, изменить его нельзя. Поскольку размер массива является статическим, структура данных выделяет память только один раз, в отличие от массива переменной длины – там структура данных должна динамически выделять память, чтобы массив мог увеличиваться или уменьшаться.

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

В этом мануале вы узнаете, как объявлять массивы, как вызывать отдельные элементы с помощью индексации, как разбивать массив на меньшие наборы.

Определение массива

Массивы определяются путем объявления размера в квадратных скобках [ ], после чего следует тип данных элементов массива. В массивах Go все элементы должны относиться к одному типу данных.

Читайте также: Основные типы данных в Go

После типа данных нужно объявить индивидуальные значения элементов массива в фигурных скобках { }.

Вот так выглядит общая схема объявления массива:

[capacity]data_type{element_values}

Примечание: Важно помнить, что каждое объявление нового массива создает отдельный тип. То есть, хотя [2]int и [3]int — целочисленные элементы, у них разная емкость, и она делает эти типы данных несовместимыми.

Если вы не объявляете значения элементов массива, устанавливается значение по умолчанию — 0, то есть элементы массива будут пустыми. Для целых чисел значение по умолчанию — 0, а для строк это пустая строка.

Например, следующий массив numbers имеет три целочисленных элемента, которые еще не имеют значения:

var numbers [3]int

Если вы введете numbers, вы получите следующий вывод:

[0 0 0]

Если вы хотите присвоить значения элементов при создании массива, поместите значения в фигурные скобки. Массив строк с заданными значениями выглядит так:

[4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}

Вы можете сохранить массив в переменной и вывести его:

coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}
fmt.Println(coral)

Запустив программу с такими строками, вы получите следующий вывод:

[blue coral staghorn coral pillar coral elkhorn coral]

Обратите внимание, при отображении между элементами в массиве нет разграничения, а потому сложно определить, где заканчивается один элемент и начинается другой. Поэтому иногда полезно использовать функцию fmt.Printf, которая может форматировать строки перед их выводом на экран. Используйте оператор %q с этой командой, чтобы функция добавила кавычки вокруг значений:

fmt.Printf("%q\n", coral)

В итоге получится:

["blue coral" "staghorn coral" "pillar coral" "elkhorn coral"]

Теперь каждый элемент заключен в кавычки. Оператор \n позволяет форматировщику добавить строку в конце.

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

Индексация массивов

Каждый элемент в массиве можно вызвать индивидуально – это делается с помощью индексов. Каждому элементу присваивается номер индекса, который является значением типа int. Индексация начинается с 0.

Индексация массива coral из предыдущего примера выглядит следующим образом:

«blue coral» «staghorn coral» «pillar coral» «elkhorn coral»
0 1 2 3

Первый элемент, строка ‘blue coral’, имеет индекс 0, а последний элемент, ‘elkhorn coral’, имеет индекс 3.

Вы можете вызвать дискретный элемент массива, ссылаясь на его индекс в скобках после переменной, в которой хранится массив:

fmt.Println(coral[2])

Это выведет следующее:

pillar coral

Индексы этого массива находятся в диапазоне от 0 до 3, поэтому для вызова любого из элементов по отдельности и присвоения им значения вы можете ссылаться на индекс следующим образом:

coral[0] = "blue coral"
coral[1] = "staghorn coral"
coral[2] = "pillar coral"
coral[3] = "elkhorn coral"

Если в этом массиве вы попробуете вызвать индекс вне этого диапазона, Go сочтет операцию недействительной:

fmt.Println(coral[22])
invalid array index 22 (out of bounds for 4-element array)

При индексации массива всегда используются положительные числа. В отличие от некоторых других языков, которые позволяют индексировать отрицательными числами, в Go приведет к ошибке:

fmt.Println(coral[-1])
invalid array index -1 (index must be non-negative)

Срез массива

Вы можете вызвать подраздел значений в массиве, используя индексы для определения начальной и конечной точек. Это называется срез массива. Вы можете сделать это, создав диапазон индексов, разделенных двоеточием, вот так:

[first_index: second_index]

Допустим, вам нужно просто вывести средние элементы массива coral, без первого и последнего элемента. Вы можете сделать это, создав срез, начинающийся с индекса 1 и заканчивающийся непосредственно до индекса 3:

fmt.Println(coral[1:3])

Запуск программы с этой строкой выведет:

[staghorn coral pillar coral]

При создании среза, как в [1: 3], первый индекс – это тот элемент, где начинается срез (включительно), а второй индекс — сумма первого индекса и общего количества элементов, которое вы хотите извлечь:

array[starting_index : (starting_index + length_of_slice)]

В этом случае мы указали второй элемент (его индекс 1) в качестве отправной точки и в итоге вызвали два элемента. Вот как будет выглядеть расчет:

array[1 : (1 + 2)]

Вот как мы пришли к этой записи:

coral[1:3]

Если в качестве начальной или конечной точки среза вы хотите установить начало или конец массива, вы можете пропустить одно из чисел в синтаксисе [first_index: second_index]. Например, если вы хотите вывести первые три элемента из массива coral – это  «blue coral», «staghorn coral», and «pillar coral» – вы можете сделать это, набрав:

fmt.Println(coral[:3])

Это выведет:

[blue coral staghorn coral pillar coral]

Так вы вывели массив с начала и до элемента с индексом 3 (исключительно).

Чтобы включить все элементы до конца массива, нужно изменить синтаксис:

fmt.Println(coral[1:])

Это дало бы следующее:

[staghorn coral pillar coral elkhorn coral]

Функции массивов

В Go есть len () – это встроенная функция, которая помогает работать с массивами. Как и в случае со строками, она позволяет вычислить длину массива, получив массив в качестве параметра.

Например, чтобы узнать, сколько элементов в массиве coral, нужно использовать:

len(coral)

Если вы выведете длину массива coral, вы получите следующий вывод:

4

Длина массива 4 имеет тип данных int, это верно, поскольку массив coral имеет четыре элемента:

coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}

Если у вас есть массив целых чисел с большим количеством элементов, вы также можете использовать функцию len():

numbers := [13]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
fmt.Println(len(numbers))
13

В этих образцах массивов, конечно, относительно мало элементов. Функция len() особенно полезна при определении количества элементов в очень больших массивах.

Теперь, когда вы знаете, как использовать len() для вывода длины массива, давайте посмотрим, чем массивы отличаются от другой структуры данных – срезов.

Массивы и срезы – в чем разница?

Как упоминалось ранее, основное отличие между массивом и срезом состоит в том, что размер массива нельзя изменить, а размер среза – можно. То есть вы можете изменять значения элементов в массиве, но не можете сделать массив больше или меньше после того, как он был определен. А длина среза может изменяться.

Давайте рассмотрим наш пример:

coral := [4]string{"blue coral", "staghorn coral", "pillar coral", "elkhorn coral"}

Предположим, нам нужно добавить в этот массив элемент «black coral». Если вы попытаетесь использовать функцию append() в массиве:

coral = append(coral, "black coral")

Вы получите сообщение об ошибке:

first argument to append must be slice; have [4]string

Если вы создали массив и хотите, чтобы он имел переменную длину, вы можете преобразовать его в срез.

Чтобы преобразовать массив в срез, используйте процесс, который мы описали в разделе «Срез массива»: при этом вам нужно выбрать все элементы массива без исключения. Это значит, вам нужно пропустить оба индексных номера, которые определяют конечные точки:

coral[:]

Имейте в виду, вы не можете преобразовать переменную coral в срез, поскольку в Go тип переменных нельзя менять. Чтобы обойти это, вы можете скопировать все содержимое массива в новую переменную в виде среза:

coralSlice := coral[:]

Если вы введете coralSlice, вы получите следующий вывод:

[blue coral staghorn coral pillar coral elkhorn coral]

Теперь попробуйте использовать функцию append() с только что преобразованным срезом:

newSlice := append(coralSlice, "black coral")
fmt.Printf("%q\n", newSlice)

Это выведет срез с добавленным элементом:

["blue coral" "staghorn coral" "pillar coral" "elkhorn coral" "black coral"]

Заключение

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

Tags: ,