Многомерные массивы в Golang

GolangGolangBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В предыдущем разделе мы узнали о базовом использовании массивов.

Какие типы элементов могут быть в массиве?

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

Что происходит, если элементами массива являются массивы?

Тогда у нас получается многомерный массив:

Многомерный массив

Как показано на рисунке выше, фиолетовый прямоугольник представляет наш исходный массив.

Каждый элемент в массиве фиолетового прямоугольника является новым массивом (красный прямоугольник).

Основные концепции:

  • Определение двумерного массива
  • Инициализация двумерного массива
  • Перебор двумерного массива
  • Использование многомерных массивов

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("Golang")) -.-> go/FunctionsandControlFlowGroup(["Functions and Control Flow"]) go(("Golang")) -.-> go/BasicsGroup(["Basics"]) go(("Golang")) -.-> go/DataTypesandStructuresGroup(["Data Types and Structures"]) go/BasicsGroup -.-> go/variables("Variables") go/DataTypesandStructuresGroup -.-> go/slices("Slices") go/FunctionsandControlFlowGroup -.-> go/for("For") go/FunctionsandControlFlowGroup -.-> go/functions("Functions") go/FunctionsandControlFlowGroup -.-> go/range("Range") subgraph Lab Skills go/variables -.-> lab-149076{{"Многомерные массивы в Golang"}} go/slices -.-> lab-149076{{"Многомерные массивы в Golang"}} go/for -.-> lab-149076{{"Многомерные массивы в Golang"}} go/functions -.-> lab-149076{{"Многомерные массивы в Golang"}} go/range -.-> lab-149076{{"Многомерные массивы в Golang"}} end

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

Общее определение

Вы помните, как мы определяем обычный массив?

var variableName [elementCount]variableType

Тогда как мы можем определить двумерный массив с использованием этого самого наивного метода?

var variableName [elementCount][elementCount]variableType

Единственное отличие заключается в том, что мы добавляем еще один [elementCount] перед исходным [elementCount], чтобы сформировать формат [elementCount][elementCount].

Например, если мы хотим определить двумерный массив с именем a с емкостью 10*10 типа int, мы можем использовать следующий синтаксис:

var a [10][10]int

Краткое определение

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

Мы можем использовать := для объявления массива, включая многомерные массивы.

Например:

a := [10][10]int{}

Таким образом, мы определяем целочисленный двумерный массив размером 10*10.

Инициализация двумерного массива

Как и одномерные массивы, двумерный массив можно инициализировать следующими способами:

  • Список инициализации
  • Инициализация с выводом длины
  • Инициализация с указанием значений по индексам

Вы помните, как использовать эти методы?

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

В следующем разделе мы повторим эти три метода инициализации и применим их к двумерным массивам.

Инициализация двумерного массива с использованием списка инициализации

Продолжим использовать файл array.go, созданный в предыдущем разделе. Если вы не сохранили свою работу, вы можете создать файл следующим образом:

touch ~/project/array.go

Напишите следующий код в файле array.go:

package main

import "fmt"

func main() {
    // Automatically initialized to 0
    var simpleArray [3][3]int
    // Initialize using specified initial values, use default values for the missing elements
    var numArray = [3][3]int{{1, 2, 3}, {2, 3, 4}}
    // Initialize using specified initial values
    var cityArray = [2][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
    fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
    fmt.Println(numArray)    // [[1 2 3] [2 3 4] [0 0 0]]
    fmt.Println(cityArray)   // [[London Chengdu] [Paris Boston]]
}

В приведенном выше коде показаны три способа инициализации двумерного массива с использованием списка инициализации.

Запустите код с помощью следующей команды:

go run ~/project/array.go

Вывод будет следующим:

[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3] [2 3 4] [0 0 0]]
[[London Chengdu] [Paris Boston]]

Вы можете изменить значения и повторить методы инициализации одномерных массивов.

Инициализация двумерного массива с выводом длины

В двумерном массиве мы можем использовать метод вывода длины для его инициализации, как и в одномерных массивах.

Напишите следующий код в файле array.go:

package main

import "fmt"

func main() {
    // Automatically initialized to 0
    var simpleArray [3][3]int
    // Initialize using specified initial values, use default values for the missing elements
    var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}, {0}}
    // Initialize using specified initial values
    var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
    fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
    fmt.Println(numArray)    // [[1 2 3 3] [2 3 4 3] [0]]
    fmt.Println(cityArray)   // [[London Chengdu] [Paris Boston]]
}

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

go run ~/project/array.go

Вывод будет таким же, как при использовании списка инициализации.

[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3 3] [2 3 4 3] [0]]
[[London Chengdu] [Paris Boston]]

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

Например:

var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}}

Этот код корректен, но следующие два варианта ошибочны:

var numArray = [][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}
var numArray = [...][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}

Кроме того, сравним numArray и cityArray.

Мы видим, что в cityArray мы указываем второй параметр размера двумерного массива, как показано ниже:

var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}

Это означает, что мы указываем размер каждого подмассива как 2 при инициализации.

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

Если количество заданных значений превышает указанный размер, возникнет ошибка.

Инициализация двумерного массива с указанием значений по индексам

Как и в одномерных массивах, в двумерных массивах также можно использовать инициализацию с указанием значений по индексам. Процесс аналогичен.

Напишите следующий код в файле array.go:

package main

import "fmt"

func main() {
    a := [...][]int{1: {1, 2, 3}, 3: {4, 7, 9}}
    fmt.Println(a)                  // [[] [1 2 3] [] [4 7 9]]
    fmt.Printf("Type of array a: %T\n", a) // Type of array a: [4][]int
}
go run ~/project/array.go

Вывод будет следующим:

[[] [1 2 3] [] [4 7 9]]
Type of array a: [4][]int

В приведенном выше коде массив a определен как двумерный массив с неопределенной длиной. Значение [1 2 3] присваивается массиву с индексом 1, а значение [4 7 9] - массиву с индексом 3.

В случае автоматического вывода длины тип массива a равен [4][]int.

Обход двумерного массива

Как обойти двумерный массив?

Когда мы изучали одномерные массивы, использовали два метода для обхода массивов: с использованием ключевого слова range и с использованием индексов.

Теперь применим эти два метода для обхода двумерного массива.

Напишите следующий код в файле array.go:

package main

import "fmt"

func main() {
    a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
    // Method 1: using the range keyword
    fmt.Println("Traversing the Two-Dimensional Array Using the range Keyword")
    for index, value := range a {
        for i, j := range value {
            fmt.Println(index, i, j)
        }
    }
    // Method 2: using index numbers
    fmt.Println("\nTraversing the Two-Dimensional Array Using Index Numbers")
    for i := 0; i < len(a); i++ {
        for j := 0; j < len(a[i]); j++ {
            fmt.Println(i, j, a[i][j])
        }
    }
}

В приведенном выше коде показаны два способа обхода двумерного массива.

go run ~/project/array.go

Вывод будет следующим:

Traversing the Two-Dimensional Array Using the range Keyword
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857

Traversing the Two-Dimensional Array Using Index Numbers
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857

Хотя оба метода дают одинаковые результаты, они принципиально различаются. Эти различия особенно заметны при практическом использовании массивов.

Практическое применение массивов

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

Рассмотрим небольшой пример, чтобы проиллюстрировать это.

Напишите следующий код в файле array.go:

package main

import "fmt"

func main() {
    a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
    // Method 1: using the range keyword
    fmt.Println("Traversing the Two-Dimensional Array Using the range Keyword")
    for _, value := range a {
        for _, j := range value {
            fmt.Println(j)
        }
    }
    fmt.Println(a)
    // Method 2: using index numbers
    fmt.Println("\nTraversing the Two-Dimensional Array Using Index Numbers")
    for i := 0; i < len(a); i++ {
        for j := 0; j < len(a[i]); j++ {
            fmt.Println(a[i][j])
            a[i][j] = 0
        }
    }
    fmt.Println(a)
}

В приведенном выше коде показано, как с использованием двух разных методов установить все значения в двумерном массиве равными 0.

go run ~/project/array.go

Вывод программы:

Traversing the Two-Dimensional Array Using the range Keyword
123
321
222
404
501
503
857
419
857
[[123 321 222] [404 501 503] [857 419 857]]

Traversing the Two-Dimensional Array Using Index Numbers
123
321
222
404
501
503
857
419
857
[[0 0 0] [0 0 0] [0 0 0]]

Мы видим, что при использовании ключевого слова range для обхода массива изменение его значений не имеет эффекта. Однако, после обхода массива с использованием индексов изменение его значений имеет место. Это происходит потому, что при использовании ключевого слова range для обхода массива переменная цикла j на самом деле является копией значения массива. Изменение значения копии не влияет на исходный массив a. В то же время, изменение значения исходного массива является допустимым при обходе массива с использованием индексов.

Расширение до многомерного массива

Иногда нам нужно использовать трехмерный или даже четырехмерный массив.

Расширение до массивов более высокой размерности не сильно отличается от перехода от одномерного массива к двумерному.

Возьмем простой пример:

package main

import "fmt"

func main() {
    a := [2][2][2]int{}
    for i := 0; i < 2; i++ {
        for j := 0; j < 2; j++ {
            for k := 0; k < 2; k++ {
                a[i][j][k] = 1
            }
        }
    }
    fmt.Println(a)
}

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

go run ~/project/array.go

Вывод будет следующим:

[[[1 1] [1 1]] [[1 1] [1 1]]]

Однако, массивы более высокой размерности не часто используются. Поэтому достаточно просто понять концепцию.

Итоги

В этом практическом занятии (лабораторной работе) мы изучили следующее:

  • Два способа определения двумерного массива
  • Три способа инициализации двумерного массива
  • Два способа обхода двумерного массива
  • Различия между двумя методами обхода в практическом использовании
  • Обзор использования многомерных массивов