Arreglos multidimensionales en Go
Un arreglo multidimensional es un arreglo de arreglos; se puede imaginar comotabla de datosomatriz, donde se puede acceder a cada elemento mediante múltiples índices.
En Go, los arreglos multidimensionales se pueden usar para procesar información estructurada, como datos tabulares, operaciones con matrices o tableros de juego.
Conceptos básicos
- Arreglo unidimensional: como puntos en una línea recta, solo se necesita una coordenada (índice) para encontrar un elemento específico.
- Arreglo bidimensional: como una tabla, se necesitan dos coordenadas: fila y columna.
- Arreglo tridimensional: como un cubo, se necesitan tres coordenadas: largo, ancho y alto.
- Dimensiones superiores: en teoría puede haber muchas dimensiones, pero en la programación real, las bidimensionales y tridimensionales son las más usadas.
Se puede comparar con nuestras plazas de aparcamiento habituales:
- Arreglo unidimensional= una fila de plazas de aparcamiento (solo se necesita el número de plaza)
- Arreglo bidimensional= un aparcamiento de varios niveles (se necesitan el nivel y el número de plaza)
- Arreglo tridimensional= varios aparcamientos de varios niveles (se necesitan el número de aparcamiento, el nivel y el número de plaza)
Go admite arreglos multidimensionales. A continuación se muestran las formas habituales de declararlos:
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
Declaración e inicialización:
// Declarar un arreglo bidimensional var nombreArreglo [numFilas][numColumnas]tipoElemento // Declarar e inicializar var nombreArreglo [numFilas][numColumnas]tipoElemento = [numFilas][numColumnas]tipoElemento{valoresIniciales}El siguiente ejemplo declara un arreglo entero tridimensional:
var threedim [5][10][4]int
Arreglo bidimensional
El arreglo bidimensional es el más simple de los arreglos multidimensionales; en esencia, está compuesto por arreglos unidimensionales. La forma de definir un arreglo bidimensional es la siguiente:
var arrayName [ x ][ y ] variable_type
variable_type es el tipo de dato en Go, arrayName es el nombre del arreglo. Un arreglo bidimensional puede considerarse una tabla, donde x es la fila e y es la columna. La siguiente figura muestra un arreglo bidimensional a de tres filas y cuatro columnas:

A los elementos de un arreglo bidimensional se puede acceder mediantea[ i ][ j ]para acceder.
Ejemplo
import "fmt"
func main() {
// Paso 1: Crear el arreglo
values := [][]int{}
// Paso 2: Usar la función append() para agregar dos arreglos unidimensionales como filas al arreglo bidimensional vacío
row1 := []int{1, 2, 3}
row2 := []int{4, 5, 6}
values = append(values, row1)
values = append(values, row2)
// Paso 3: Mostrar las dos filas de datos
fmt.Println("Row 1")
fmt.Println(values[0])
fmt.Println("Row 2")
fmt.Println(values[1])
// Paso 4: Acceder al primer elemento
fmt.Println("El primer elemento es:")
fmt.Println(values[0][0])
}
El resultado de ejecutar el ejemplo anterior es:
Row 1 [1 2 3] Row 2 [4 5 6] 第一个元素为: 1
Inicializar un arreglo bidimensional
Los arreglos multidimensionales se pueden inicializar con llaves. El siguiente ejemplo es un arreglo bidimensional de 3 filas y 4 columnas:
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
Nota:En el código anterior, la penúltima línea}debe tener una coma, porque la última línea}no puede estar en una línea por sí sola; también se puede escribir así:a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}} /* 第三行索引为 2 */
El siguiente ejemplo inicializa un arreglo bidimensional de 2 filas y 2 columnas:
Ejemplo
import "fmt"
func main() {
// Crear un arreglo bidimensional
sites := [2][2]string{}
// Agregar elementos al arreglo bidimensional
sites[0][0] = "Google"
sites[0][1] = "Ejemplo"
sites[1][0] = "Taobao"
sites[1][1] = "Weibo"
// Mostrar el resultado
fmt.Println(sites)
}
El resultado de ejecutar el ejemplo anterior es:
[[Google Ejemplo] [Taobao Weibo]]
Acceder a un arreglo bidimensional
Se accede a un arreglo bidimensional especificando coordenadas, como el índice de fila y el índice de columna; por ejemplo:
val := a[2][3] o var value int = a[2][3]
El ejemplo anterior accede al cuarto elemento de la tercera fila del arreglo bidimensional val.
Se pueden usar bucles anidados para mostrar los elementos de un arreglo bidimensional:
Ejemplo
import "fmt"
func main() {
/* Arreglo - 5 filas y 2 columnas */
var a = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
var i, j int
/* Imprimir los elementos del arreglo */
for i = 0; i < 5; i++ {
for j = 0; j < 2; j++ {
fmt.Printf("a[%d][%d] = %d\n", i,j, a[i][j] )
}
}
}
El resultado de ejecutar el ejemplo anterior es:
a[0][0] = 0 a[0][1] = 0 a[1][0] = 1 a[1][1] = 2 a[2][0] = 2 a[2][1] = 4 a[3][0] = 3 a[3][1] = 6 a[4][0] = 4 a[4][1] = 8
El siguiente ejemplo crea un arreglo multidimensional con distinta cantidad de elementos en cada dimensión:
Ejemplo
import "fmt"
func main() {
// Crear un arreglo bidimensional vacío
animals := [][]string{}
// Crear tres arreglos unidimensionales, cada uno con longitud diferente
row1 := []string{"fish", "shark", "eel"}
row2 := []string{"bird"}
row3 := []string{"lizard", "salamander"}
// Usar la función append() para agregar los arreglos unidimensionales al arreglo bidimensional
animals = append(animals, row1)
animals = append(animals, row2)
animals = append(animals, row3)
// Imprimir en un bucle
for i := range animals {
fmt.Printf("Row: %v\n", i)
fmt.Println(animals[i])
}
}
El resultado de ejecutar el ejemplo anterior es:
Row: 0 [fish shark eel] Row: 1 [bird] Row: 2 [lizard salamander]
Acceder y modificar elementos del arreglo
Acceder a los elementos
Ejemplo
import "fmt"
func main() {
// Crear una matriz de 3x3
matrix := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
// Acceder a un solo elemento
fmt.Println("Fila 1, Columna 2:", matrix[0][1]) // Imprime: 2
fmt.Println("Fila 3, Columna 3:", matrix[2][2]) // Imprime: 9
// Acceder a toda la fila
fmt.Println("Fila 2:", matrix[1]) // Imprime: [4 5 6]
// Recorrer todos los elementos
fmt.Println("\nRecorrer todos los elementos:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j])
}
}
}
Modificar elementos
Ejemplo
import "fmt"
func main() {
// Crear una matriz de ceros de 2x2
var grid [2][2]int
fmt.Println("Matriz antes de modificar:", grid)
// Modificar el elemento en una posición específica
grid[0][0] = 10
grid[0][1] = 20
grid[1][0] = 30
grid[1][1] = 40
fmt.Println("Matriz después de modificar:", grid)
// Modificar una fila en lote
grid[0] = [2]int{100, 200}
fmt.Println("Después de modificar la primera fila:", grid)
}
Arreglos tridimensionales y de dimensiones superiores
Ejemplo de arreglo tridimensional
Ejemplo
import "fmt"
func main() {
// Declarar un arreglo tridimensional de 2x3x4
// Se puede entender como: 2 planos, cada plano con 3 filas y 4 columnas
var cube [2][3][4]int
// Inicializar el arreglo tridimensional
cube = [2][3][4]int{
{ // El primer plano
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
},
{ // El segundo plano
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24},
},
}
// Acceder a los elementos del arreglo tridimensional
fmt.Println("cube[0][1][2] =", cube[0][1][2]) // Salida: 7
fmt.Println("cube[1][2][3] =", cube[1][2][3]) // Salida: 24
// Recorrer el arreglo tridimensional
fmt.Println("\nContenido del arreglo tridimensional:")
for i := 0; i < 2; i++ {
fmt.Printf("Plano %d:\n", i)
for j := 0; j < 3; j++ {
for k := 0; k < 4; k++ {
fmt.Printf("%3d ", cube[i][j][k])
}
fmt.Println()
}
fmt.Println()
}
}
Vea la siguiente figura:

Operaciones comunes con arreglos multidimensionales
1. Recorrer con range
Ejemplo
import "fmt"
func main() {
// Crear un arreglo bidimensional
scores := [3][4]int{
{85, 90, 78, 92},
{88, 76, 95, 89},
{92, 85, 88, 90},
}
fmt.Println("Tabla de calificaciones de estudiantes:")
// Usar range para recorrer un arreglo bidimensional
for i, row := range scores {
fmt.Printf("Calificaciones del estudiante %d: ", i+1)
for j, score := range row {
fmt.Printf("%d ", score)
// Si se necesitan tanto el índice como el valor
_ = j // Evitar la advertencia de variable no utilizada
}
fmt.Println()
}
// Solo importa el valor, no el índice
total := 0
count := 0
for _, row := range scores {
for _, score := range row {
total += score
count++
}
}
fmt.Printf("\nPromedio: %.2f\n", float64(total)/float64(count))
}
2. Obtener la longitud del arreglo
Ejemplo
import "fmt"
func main() {
// Crear un arreglo multidimensional irregular
jagged := [3][3]int{
{1, 2, 3},
{4, 5},
{6, 7, 8, 9}, // Nota: Esto causará un error de compilación, porque cada fila debe tener la misma longitud
}
// Ejemplo correcto: obtener las dimensiones del arreglo
matrix := [4][5]int{}
// Obtener el número de filas
rows := len(matrix)
fmt.Println("Número de filas:", rows) // Salida: 4
// Obtener el número de columnas de la primera fila (todas las filas tienen la misma longitud)
cols := len(matrix[0])
fmt.Println("Número de columnas:", cols) // Salida: 5
// Obtener el número total de elementos
totalElements := rows * cols
fmt.Println("Número total de elementos:", totalElements) // Salida: 20
}
3. Comparación de arreglos
Ejemplo
import "fmt"
func main() {
// Crear dos arreglos bidimensionales idénticos
a := [2][2]int{{1, 2}, {3, 4}}
b := [2][2]int{{1, 2}, {3, 4}}
c := [2][2]int{{1, 2}, {3, 5}}
// Los arreglos se pueden comparar directamente (solo cuando las dimensiones son exactamente iguales)
fmt.Println("a == b:", a == b) // Salida: true
fmt.Println("a == c:", a == c) // Salida: false
// Nota: los arrays de diferentes dimensiones no se pueden comparar
// d := [2][3]int{{1, 2, 3}, {4, 5, 6}}
// fmt.Println(a == d) // Error de compilación: tipos no coinciden
}
Casos de uso prácticos
Escenario 1: Tablero de juego (tres en raya)
Ejemplo
import "fmt"
func main() {
// Inicializar un tablero de tres en raya de 3x3
var board [3][3]string
// Inicializar vacío
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
board[i][j] = " "
}
}
// Simular jugada
board[0][0] = "X"
board[1][1] = "O"
board[2][2] = "X"
// Imprimir tablero
fmt.Println("Tablero de tres en raya:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf(" %s ", board[i][j])
if j < 2 {
fmt.Printf("|")
}
}
fmt.Println()
if i < 2 {
fmt.Println("---+---+---")
}
}
}
Escenario 2: Sistema de gestión de calificaciones de estudiantes
Ejemplo
import "fmt"
func main() {
// Definir: 3 estudiantes, cada uno con 4 cursos
var grades [3][4]float64
// Ingresar calificaciones de estudiantes
grades = [3][4]float64{
{85.5, 90.0, 78.5, 92.0}, // Calificaciones del estudiante 1
{88.0, 76.5, 95.0, 89.5}, // Calificaciones del estudiante 2
{92.5, 85.0, 88.5, 90.0}, // Calificaciones del estudiante 3
}
// Calcular el promedio de cada estudiante
fmt.Println("Estadísticas de calificaciones de estudiantes:")
for i, studentGrades := range grades {
sum := 0.0
for _, grade := range studentGrades {
sum += grade
}
average := sum / float64(len(studentGrades))
fmt.Printf("Estudiante %d: Promedio = %.2f\n", i+1, average)
}
// Calcular el promedio de cada curso
fmt.Println("\nPromedio de los cursos:)
for j := 0; j < 4; j++ {
sum := 0.0
for i := 0; i < 3; i++ {
sum += grades[i][j]
}
average := sum / 3.0
fmt.Printf("Curso %d: Promedio = %.2f\n", j+1, average)
}
}
Escenario 3: Procesamiento de píxeles de imagen
Ejemplo
import "fmt"
func main() {
// Simular una imagen en escala de grises simple de 3x3
// Rango de valores de cada píxel: 0 (negro) ~ 255 (blanco)
var image [3][3]int
// Inicializar la imagen (un degradado simple)
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
image[i][j] = (i + j) * 50
}
}
// Mostrar la imagen original
fmt.Println("Imagen original:")
displayImage(image)
// Procesamiento de imagen: aumentar brillo
fmt.Println("\nImagen con brillo aumentado:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
// Aumentar el brillo, pero sin superar 255
newValue := image[i][j] + 50
if newValue > 255 {
newValue = 255
}
image[i][j] = newValue
}
}
displayImage(image)
}
func displayImage(img [3][3]int) {
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("%3d ", img[i][j])
}
fmt.Println()
}
}
Notas y mejores prácticas
1. La longitud del arreglo es parte del tipo
Ejemplo
import "fmt"
func main() {
// ¡Estos dos son de tipos diferentes!
var a [2][3]int
var b [3][2]int
// El siguiente código dará error de compilación
// a = b // Error: tipos no coinciden
fmt.Printf("Tipo de a: %T\n", a) // [2][3]int
fmt.Printf("Tipo de b: %T\n", b) // [3][2]int
}
2. Tipo de valor vs tipo de referencia
Ejemplo
import "fmt"
func main() {
// Los arrays son tipos de valor
original := [2][2]int{{1, 2}, {3, 4}}
// La asignación crea una copia
copy := original
// Modificar la copia no afecta al array original
copy[0][0] = 100
fmt.Println("Array original:", original) // [[1 2] [3 4]]
fmt.Println("Array copia:", copy) // [[100 2] [3 4]]
// Si se necesita semántica de referencia, se pueden usar slices (se presentará en artículos posteriores)
}
3. Consideraciones de rendimiento
- Memoria contigua: Los arrays multidimensionales se almacenan de forma continua en memoria, con acceso rápido
- Tamaño fijo: La longitud del array se determina en tiempo de compilación y no se puede cambiar dinámicamente
- Escenarios adecuados: Cuando el tamaño de los datos es conocido y fijo, los arrays son la mejor opción
Preguntas frecuentes
P1: ¿Cuál es la diferencia entre un arreglo multidimensional y un slice anidado?
| Característica | Array multidimensional | Slice anidado |
|---|---|---|
| Tamaño | Fijo, determinado en tiempo de compilación | Dinámico, variable en tiempo de ejecución |
| Memoria | Asignación contigua | Puede ser no contigua |
| Rendimiento | Acceso rápido | Un poco más lento, con sobrecarga adicional |
| Escenario de uso | Tamaño de datos conocido | Tamaño de datos variable |
P2: ¿Cómo crear un arreglo bidimensional irregular?
Los arrays de Go requieren que cada fila tenga la misma longitud. Si necesitas una estructura irregular, debes usar slices:
Ejemplo
irregular := [][]int{
{1, 2, 3},
{4, 5}, // Esta fila solo tiene 2 elementos
{6, 7, 8, 9}, // Esta fila tiene 4 elementos
}
P3: ¿Puede un arreglo multidimensional usarse como parámetro de función?
Sí, pero ten en cuenta que los arrays son tipos de valor; pasar arrays grandes tiene un costo de rendimiento:
Ejemplo
// Procesar la matriz...
matrix[0][0] = 100
return matrix
}
// Una mejor opción es usar punteros o slices
func processMatrixPtr(matrix *[3][3]int) {
matrix[0][0] = 100
}
Arreglos en Go