Comprendre les tableaux et les tranches in Go

introduction

Dans Go,arrays etslices sont desdata structures constitués d'une séquence ordonnée d'éléments. Ces collections de données sont très utiles lorsque vous souhaitez utiliser plusieurs valeurs associées. Ils vous permettent de conserver des données qui appartiennent ensemble, de condenser votre code et d'effectuer les mêmes méthodes et opérations sur plusieurs valeurs à la fois.

Bien que les tableaux et les tranches dans Go soient tous les deux des séquences ordonnées d'éléments, il existe des différences significatives entre les deux. Unarray dans Go est undata structure qui consiste en une séquence ordonnée d'éléments dont la capacité est définie au moment de la création. Une fois qu'un tableau a alloué sa taille, celle-ci ne peut plus être modifiée. Aslice, en revanche, est une version de longueur variable d'un tableau, offrant plus de flexibilité aux développeurs utilisant ces structures de données. Les tranches constituent ce que vous penseriez être des tableaux dans d'autres langues.

Compte tenu de ces différences, il existe des situations spécifiques dans lesquelles vous utiliseriez l'une sur l'autre. Si vous débutez dans Go, il peut être difficile de déterminer quand les utiliser. Bien que la polyvalence des tranches en fasse un choix plus approprié dans la plupart des situations, il existe des cas spécifiques dans lesquels des tableaux peuvent optimiser les performances de votre programme.

Cet article traite en détail des tableaux et des tranches, ce qui vous fournira les informations nécessaires pour faire le choix approprié lors du choix entre ces types de données. En outre, vous allez examiner les méthodes les plus courantes pour déclarer et utiliser des tableaux et des tranches. Le didacticiel va tout d’abord fournir une description des tableaux et de leur manipulation, puis une explication des tranches et de leurs différences.

Tableaux

Les tableaux sont des structures de données de collection avec un nombre défini d'éléments. Étant donné que la taille d'un tableau est statique, la structure de données n'a besoin d'allouer de mémoire qu'une seule fois, par opposition à une structure de données de longueur variable qui doit allouer de manière dynamique de la mémoire pour qu'elle puisse devenir plus grande ou plus petite à l'avenir. Bien que la longueur fixe des tableaux puisse les rendre plus difficiles à utiliser, l'allocation de mémoire ponctuelle peut augmenter la vitesse et les performances de votre programme. Pour cette raison, les développeurs utilisent généralement des tableaux lors de l'optimisation de programmes dans des cas où la structure de données n'aura jamais besoin d'une quantité variable d'éléments.

Définir un tableau

Les tableaux sont définis en déclarant la taille du tableau entre crochets[ ], suivi du type de données des éléments. Un tableau dans Go doit avoir tous ses éléments identiquesdata type. Après le type de données, vous pouvez déclarer les valeurs individuelles des éléments du tableau entre accolades{ }.

Voici le schéma général pour déclarer un tableau:

[capacity]data_type{element_values}

[.note] #Note: Il est important de se rappeler que chaque déclaration d'un nouveau tableau crée un type distinct. Ainsi, bien que[2]int et[3]int aient tous deux des éléments entiers, leurs longueurs différentes rendent leurs types de données incompatibles.
#

Si vous ne déclarez pas les valeurs des éléments du tableau, la valeur par défaut est zéro, ce qui signifie que les éléments du tableau seront vides. Pour les entiers, cela est représenté par0, et pour les chaînes, cela est représenté par une chaîne vide.

Par exemple, le tableaunumbers suivant contient trois éléments entiers qui n'ont pas encore de valeur:

var numbers [3]int

Si vous imprimeznumbers, vous recevrez la sortie suivante:

Output[0 0 0]

Si vous souhaitez affecter les valeurs des éléments lors de la création du tableau, placez-les entre accolades. Un tableau de chaînes avec des valeurs définies ressemble à ceci:

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

Vous pouvez stocker un tableau dans une variable et l'imprimer:

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

L'exécution d'un programme avec les lignes précédentes vous donnerait la sortie suivante:

Output[blue coral staghorn coral pillar coral elkhorn coral]

Notez qu’il n’ya aucune délimitation entre les éléments du tableau lorsqu’il est imprimé, ce qui rend difficile de dire où un élément se termine et un autre commence. Pour cette raison, il est parfois utile d'utiliser la fonctionfmt.Printf à la place, qui peut formater les chaînes avant de les imprimer à l'écran. Fournissez le verbe%q avec cette commande pour indiquer à la fonction de mettre des guillemets autour des valeurs:

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

Cela se traduira par:

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

Maintenant, chaque article est cité. Le verbe demande au formateur d'ajouter un retour de ligne à la fin.

Avec une idée générale de la façon de déclarer des tableaux et de leur composition, vous pouvez maintenant apprendre à spécifier des éléments dans un tableau avec un numéro d'index.

Indexation des tableaux (et des tranches)

Chaque élément d'un tableau (et d'une tranche) peut être appelé individuellement via l'indexation. Chaque élément correspond à un numéro d'index, qui est une valeurint à partir du numéro d'index0 et en comptant.

Nous allons utiliser un tableau dans les exemples suivants, mais vous pouvez également utiliser une tranche car ils sont identiques dans leur indexation.

Pour le tableaucoral, la répartition de l'index ressemble à ceci:

"Corail bleu" "Corail staghorn" "Corail pilier" «Corail elkhorn»

0

1

2

3

Le premier élément, la chaîne"blue coral", commence à l'index0 et la tranche se termine à l'index3 avec l'élément"elkhorn coral".

Chaque élément d’une tranche ou d’un tableau ayant un numéro d’indice correspondant, nous pouvons y accéder et les manipuler de la même manière que nous le pouvons avec d’autres types de données séquentielles.

Nous pouvons maintenant appeler un élément discret de la tranche en faisant référence à son numéro d'index:

fmt.Println(coral[1])
Outputstaghorn coral

Les numéros d'index pour cette tranche vont de0-3, comme indiqué dans le tableau précédent. Donc, pour appeler chacun des éléments individuellement, nous nous référons aux indices suivants:

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

Si nous appelons le tableaucoral avec un numéro d'index quelconque supérieur à3, il sera hors de portée car il ne sera pas valide:

fmt.Println(coral[18])
Outputpanic: runtime error: index out of range

Lors de l'indexation d'un tableau ou d'une tranche, vous devez toujours utiliser un nombre positif. Contrairement à certaines langues qui vous permettent d'indexer à l'envers avec un nombre négatif, le faire dans Go entraînera une erreur:

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

Nous pouvons concaténer des éléments de chaîne dans un tableau ou une tranche avec d'autres chaînes en utilisant l'opérateur+:

fmt.Println("Sammy loves " + coral[0])
OutputSammy loves blue coral

Nous avons pu concaténer l'élément de chaîne au numéro d'index0 avec la chaîne"Sammy loves ".

Avec les numéros d'index correspondant aux éléments d'un tableau ou d'une tranche, nous pouvons accéder à chaque élément de manière discrète et travailler avec ces éléments. Pour illustrer cela, nous verrons ensuite comment modifier un élément à un certain index.

Modifier des éléments

Nous pouvons utiliser l'indexation pour modifier des éléments dans un tableau ou une tranche en définissant un élément d'index numéroté égal à une valeur différente. Cela nous donne un plus grand contrôle sur les données de nos tranches et de nos tableaux et nous permettra de manipuler par programme des éléments individuels.

Si nous voulons changer la valeur de chaîne de l'élément à l'index1 du tableaucoral de"staghorn coral" à"foliose coral", nous pouvons le faire comme ceci:

coral[1] = "foliose coral"

Maintenant, lorsque nous imprimonscoral, le tableau sera différent:

fmt.Printf("%q\n", coral)
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral"]

Maintenant que vous savez manipuler les éléments individuels d'un tableau ou d'une tranche, examinons quelques fonctions qui vous donneront plus de flexibilité lorsque vous travaillerez avec des types de données de collection.

Comptage des éléments aveclen()

Dans Go,len() est une fonction intégrée conçue pour vous aider à travailler avec des tableaux et des tranches. Comme avec les chaînes, vous pouvez calculer la longueur d'un tableau ou d'une tranche en utilisantlen() et en passant le tableau ou la tranche en tant que paramètre.

Par exemple, pour trouver le nombre d'éléments dans le tableaucoral, vous utiliseriez:

len(coral)

Si vous imprimez la longueur du tableaucoral, vous recevrez le résultat suivant:

Output4

Cela donne la longueur du tableau4 dans le type de donnéesint, ce qui est correct car le tableaucoral a quatre éléments:

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

Si vous créez un tableau d'entiers avec plus d'éléments, vous pouvez également utiliser la fonctionlen() là-dessus:

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

Cela donnerait la sortie suivante:

Output13

Bien que ces exemples de tableaux contiennent relativement peu d'éléments, la fonctionlen() est particulièrement utile pour déterminer le nombre d'éléments dans de très grands tableaux.

Nous verrons ensuite comment ajouter un élément à un type de données de collection et montrer comment, en raison de la longueur fixe des tableaux, l'ajout de ces types de données statiques génèrera une erreur.

Ajout d'éléments avecappend()

append() est une méthode intégrée dans Go qui ajoute des éléments à un type de données de collection. Cependant, cette méthode ne fonctionnera pas si elle est utilisée avec un tableau. Comme mentionné précédemment, la principale différence entre les tableaux et les tranches est que la taille d'un tableau ne peut pas être modifiée. Cela signifie que même si vous pouvez modifier les valeurs des éléments d’un tableau, vous ne pouvez pas agrandir ou réduire le tableau après sa définition.

Considérons votre tableaucoral:

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

Supposons que vous souhaitiez ajouter l'élément"black coral" à ce tableau. Si vous essayez d'utiliser la fonctionappend() avec le tableau en tapant:

coral = append(coral, "black coral")

Vous recevrez une erreur en tant que votre sortie:

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

Pour résoudre ce problème, apprenons-en davantage sur le type de données de la tranche, comment définir une tranche et comment convertir un tableau en une tranche.

Les tranches

Unslice est un type de données dans Go qui est une séquence ordonnée d'élémentsmutable ou modifiable. Comme la taille d'une tranche est variable, il est beaucoup plus flexible de les utiliser. Lorsque vous travaillez avec des collections de données pouvant nécessiter une expansion ou une contraction dans le futur, l'utilisation d'une tranche garantit que votre code ne rencontrera pas d'erreurs lors d'une tentative de manipulation de la longueur de la collection. Dans la plupart des cas, cette mutabilité vaut la réallocation de mémoire possible parfois requise par les tranches par rapport aux tableaux. Lorsque vous devez stocker de nombreux éléments ou effectuer des itérations sur des éléments et que vous souhaitez pouvoir modifier facilement ces éléments, vous souhaiterez probablement utiliser le type de données de la tranche.

Définir une tranche

Les tranches sont définies en déclarant le type de données précédé d'un ensemble vide de crochets ([]) et d'une liste d'éléments entre accolades ({}). Vous remarquerez que, contrairement aux tableaux qui nécessitent unint entre les crochets pour déclarer une longueur spécifique, une tranche n'a rien entre les crochets, ce qui représente sa longueur variable.

Créons une tranche contenant des éléments du type de données string:

seaCreatures := []string{"shark", "cuttlefish", "squid", "mantis shrimp", "anemone"}

Lorsque nous imprimons la tranche, nous pouvons voir les éléments qui sont dans la tranche:

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

Cela se traduira par:

Output["shark" "cuttlefish" "squid" "mantis shrimp" "anemone"]

Si vous souhaitez créer une tranche d'une certaine longueur sans remplir encore les éléments de la collection, vous pouvez utiliser la fonctionmake() intégrée:

oceans := make([]string, 3)

Si vous imprimiez cette tranche, vous obtiendriez:

Output["" "" ""]

Si vous souhaitez pré-allouer la mémoire pour une certaine capacité, vous pouvez passer un troisième argument àmake():

oceans := make([]string, 3, 5)

Cela ferait une tranche remise à zéro avec une longueur de3 et une capacité pré-allouée d'éléments5.

Vous savez maintenant comment déclarer une tranche. Cependant, cela ne résout pas encore l'erreur que nous avions avec le tableaucoral plus tôt. Pour utiliser la fonctionappend() aveccoral, vous devrez d'abord apprendre à découper des sections d'un tableau.

Découper des tableaux en tranches

En utilisant des numéros d'index pour déterminer les points de départ et d'arrivée, vous pouvez appeler une sous-section des valeurs d'un tableau. Cela s'appelleslicing le tableau, et vous pouvez le faire en créant une plage de numéros d'index séparés par deux points, sous la forme de[first_index:second_index]. Cependant, il est important de noter que lors de la découpe d'un tableau, le résultat est une tranche, pas un tableau.

Supposons que vous souhaitiez simplement imprimer les éléments du milieu du tableaucoral, sans le premier et le dernier élément. Vous pouvez le faire en créant une tranche commençant à l'index1 et se terminant juste avant l'index3:

fmt.Println(coral[1:3])

L'exécution d'un programme avec cette ligne donnerait ce qui suit:

Output[foliose coral pillar coral]

Lors de la création d'une tranche, comme dans[1:3], le premier nombre est l'endroit où la tranche commence (inclus), et le second nombre est la somme du premier nombre et du nombre total d'éléments que vous souhaitez récupérer:

array[starting_index : (starting_index + length_of_slice)]

Dans ce cas, vous avez appelé le deuxième élément (ou index 1) comme point de départ et appelé deux éléments au total. Voici à quoi ressemblerait le calcul:

array[1 : (1 + 2)]

Comment êtes-vous arrivé à cette notation:

coral[1:3]

Si vous souhaitez définir le début ou la fin du tableau comme point de départ ou de fin de la tranche, vous pouvez omettre l'un des nombres dans la syntaxearray[first_index:second_index]. Par exemple, si vous souhaitez imprimer les trois premiers éléments du tableaucoral - qui seraient"blue coral","foliose coral" et"pillar coral" - vous pouvez le faire en tapant:

fmt.Println(coral[:3])

Cela va imprimer:

Output[blue coral foliose coral pillar coral]

Cela a imprimé le début du tableau, s'arrêtant juste avant l'index3.

Pour inclure tous les éléments à la fin d'un tableau, vous devez inverser la syntaxe:

fmt.Println(coral[1:])

Cela donnerait la tranche suivante:

Output[foliose coral pillar coral elkhorn coral]

Cette section a traité de l'appel de parties individuelles d'un tableau en découpant des sous-sections. Ensuite, vous apprendrez à utiliser le découpage en tranches pour convertir des tableaux entiers en tranches.

Conversion d'un tableau en une tranche

Si vous créez un tableau et décidez que vous en avez besoin d'une longueur variable, vous pouvez le convertir en slice. Pour convertir un tableau en tranche, utilisez le processus de découpage que vous avez appris à l'étapeSlicing Arrays into Slices de ce didacticiel, sauf que cette fois, sélectionnez la tranche entière en omettant les deux numéros d'index qui détermineraient les points de terminaison:

coral[:]

Gardez à l’esprit que vous ne pouvez pas convertir la variablecoral en une tranche elle-même, car une fois qu’une variable est définie dans Go, son type ne peut pas être modifié. Pour contourner ce problème, vous pouvez copier l'intégralité du contenu du tableau dans une nouvelle variable sous forme de tranche:

coralSlice := coral[:]

Si vous imprimezcoralSlice, vous recevrez la sortie suivante:

Output[blue coral foliose coral pillar coral elkhorn coral]

Maintenant, essayez d'ajouter l'élémentblack coral comme dans la section tableau, en utilisantappend() avec la tranche nouvellement convertie:

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

Ceci affichera la tranche avec l'élément ajouté:

Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral"]

Nous pouvons également ajouter plus d'un élément dans une seule instructionappend():

coralSlice = append(coralSlice, "antipathes", "leptopsammia")
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia"]

Pour combiner deux tranches ensemble, vous pouvez utiliserappend(), mais vous devez développer le deuxième argument à ajouter en utilisant la syntaxe d'expansion...:

moreCoral := []string{"massive coral", "soft coral"}
coralSlice = append(coralSlice, moreCoral...)
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]

Maintenant que vous avez appris comment ajouter un élément à votre tranche, nous allons voir comment en supprimer un.

Retrait d'un élément d'une tranche

Contrairement à d'autres langages, Go ne fournit aucune fonction intégrée permettant de supprimer un élément d'une tranche. Les articles doivent être retirés d'une tranche en les découpant en tranches.

Pour supprimer un élément, vous devez découper les éléments précédents, séparer les éléments suivants, puis ajouter ces deux nouvelles tranches sans l'élément que vous souhaitez supprimer.

Sii est l'index de l'élément à supprimer, alors le format de ce processus ressemblerait à ce qui suit:

slice = append(slice[:i], slice[i+1:]...)

À partir decoralSlice, supprimons l'élément"elkhorn coral". Cet élément est situé à la position d'index de3.

coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}

coralSlice = append(coralSlice[:3], coralSlice[4:]...)

fmt.Printf("%q\n", coralSlice)
Output["blue coral" "foliose coral" "pillar coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]

Maintenant, l'élément à la position d'index3, la chaîne"elkhorn coral", n'est plus dans notre tranchecoralSlice.

Nous pouvons également supprimer une plage avec la même approche. Supposons que nous voulions supprimer non seulement l'élément"elkhorn coral", mais aussi"black coral" et"antipathes". Pour cela, nous pouvons utiliser une plage dans l'expression:

coralSlice := []string{"blue coral", "foliose coral", "pillar coral", "elkhorn coral", "black coral", "antipathes", "leptopsammia", "massive coral", "soft coral"}

coralSlice = append(coralSlice[:3], coralSlice[6:]...)

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

Ce code retirera les index3,4 et5 de la tranche:

Output["blue coral" "foliose coral" "pillar coral" "leptopsammia" "massive coral" "soft coral"]

Maintenant que vous savez comment ajouter et supprimer des éléments d’une tranche, voyons comment mesurer la quantité de données qu’une tranche peut contenir à un moment donné.

Mesure de la capacité d'une tranche aveccap()

Puisque les tranches ont une longueur variable, la fonctionlen()+`method is not the best option to determine the size of this data type. Instead, you can use the `+cap() pour apprendre la capacité d'une tranche. Cela vous indiquera le nombre d'éléments qu'une tranche peut contenir, ce qui est déterminé par la quantité de mémoire déjà allouée à la tranche.

[.note] #Note: La longueur et la capacité d'un tableau étant toujours identiques, la fonctioncap() ne fonctionnera pas sur les tableaux.
#

Une utilisation courante decap() est de créer une tranche avec un nombre prédéfini d'éléments, puis de remplir ces éléments par programme. Cela évite les allocations inutiles potentielles qui pourraient se produire en utilisantappend() pour ajouter des éléments au-delà de la capacité actuellement allouée.

Prenons le scénario où nous voulons faire une liste de nombres,0 à3. Nous pouvons utiliserappend() dans une boucle pour ce faire, ou nous pouvons pré-allouer la tranche en premier et utilisercap() pour boucler pour remplir les valeurs.

Tout d'abord, nous pouvons examiner l'utilisation deappend():

numbers := []int{}
for i := 0; i < 4; i++ {
    numbers = append(numbers, i)
}
fmt.Println(numbers)
Output[0 1 2 3]

Dans cet exemple, nous avons créé une tranche, puis créé une bouclefor qui serait itérée quatre fois. Chaque itération a ajouté la valeur actuelle de la variable de bouclei à l'index de la tranchenumbers. Toutefois, cela pourrait entraîner des allocations de mémoire inutiles susceptibles de ralentir votre programme. Lors de l'ajout à une tranche vide, chaque fois que vous appelez un ajout, le programme vérifie la capacité de la tranche. Si l'élément ajouté fait que la tranche dépasse cette capacité, le programme allouera de la mémoire supplémentaire pour en tenir compte. Cela crée une surcharge supplémentaire dans votre programme et peut entraîner une exécution plus lente.

Maintenant, remplissons la tranche sans utiliserappend() en pré-allouant une certaine longueur / capacité:

numbers := make([]int, 4)
for i := 0; i < cap(numbers); i++ {
    numbers[i] = i
}

fmt.Println(numbers)
Output[0 1 2 3]

Dans cet exemple, nous avons utilisémake() pour créer une tranche et lui avons pré-alloué les éléments4. Nous avons ensuite utilisé la fonctioncap() dans la boucle pour parcourir chaque élément remis à zéro, en remplissant chacun d'eux jusqu'à ce qu'il atteigne la capacité pré-allouée. Dans chaque boucle, nous avons placé la valeur actuelle de la variable de bouclei dans l'index de la tranchenumbers.

Alors que les stratégiesappend() etcap() sont toutes deux fonctionnellement équivalentes, l'exemplecap() évite toute allocation de mémoire supplémentaire qui aurait été nécessaire à l'aide de la fonctionappend().

Construction de tranches multidimensionnelles

Vous pouvez également définir des tranches composées d'autres tranches en tant qu'éléments, chaque liste entre crochets étant encadrée par les crochets plus grands de la tranche parente. Les collections de tranches comme celles-ci sont appeléesmultidimensional slices. Celles-ci peuvent être considérées comme décrivant des coordonnées multidimensionnelles; Par exemple, une collection de cinq tranches de six éléments de long chacune peut représenter une grille bidimensionnelle de cinq longueurs horizontales et de six hauteurs verticales.

Examinons la tranche multidimensionnelle suivante:

seaNames := [][]string{{"shark", "octopus", "squid", "mantis shrimp"}, {"Sammy", "Jesse", "Drew", "Jamie"}}

Pour accéder à un élément de cette tranche, nous devrons utiliser plusieurs index, un pour chaque dimension de la construction:

fmt.Println(seaNames[1][0])
fmt.Println(seaNames[0][0])

Dans le code précédent, on identifie d'abord l'élément à l'index0 de la tranche à l'index1, puis on indique l'élément à l'index0 de la tranche à l'index0 . Cela donnera ce qui suit:

OutputSammy
shark

Voici les valeurs d'index pour le reste des éléments individuels:

seaNames[0][0] = "shark"
seaNames[0][1] = "octopus"
seaNames[0][2] = "squid"
seaNames[0][3] = "mantis shrimp"

seaNames[1][0] = "Sammy"
seaNames[1][1] = "Jesse"
seaNames[1][2] = "Drew"
seaNames[1][3] = "Jamie"

Lorsque vous travaillez avec des tranches multidimensionnelles, il est important de garder à l’esprit que vous aurez besoin de faire référence à plus d’un numéro d’index pour pouvoir accéder à des éléments spécifiques de la tranche imbriquée appropriée.

Conclusion

Dans ce tutoriel, vous avez appris les bases du travail avec les tableaux et les tranches dans Go. Vous avez effectué plusieurs exercices pour démontrer comment les tableaux sont fixés en longueur, alors que les tranches ont une longueur variable, et vous avez découvert comment cette différence affecte les utilisations situationnelles de ces structures de données.

Pour continuer à étudier les structures de données dans Go, consultez notre article surUnderstanding Maps in Go ou explorez toute la sérieHow To Code in Go.