Introdução
Em Go, arrays (arrays) e slices (fatias) são estruturas de dados que consistem em uma sequência ordenada de elementos. essas coleções de dados são ótimas para usar quando você quer trabalhar com muitos valores relacionados. Elas permitem manter dados juntos que estão juntos, compactar seu código e executar os mesmos métodos e operações em vários valores de uma só vez.
Embora arrays e slices em Go sejam ambos sequências ordenadas de elementos, existem diferenças significativas entre os dois. Um array em Go é uma estrutura de dados que consiste em uma sequência ordenada de elementos que tem sua capacidade definida na criação. Assim que um array aloca seu tamanho, o tamanho não pode mais ser alterado. Por outro lado, um slice é uma versão com tamanho variável de um array, oferecendo mais flexibilidade para os desenvolvedores que usam essas estruturas de dados. As fatias constituem aquilo que você pensaria de arrays em outras linguagens.
Dadas essas diferenças, existem situações específicas em que você usaria uma em vez da outra. Se você está novo em Go, determinando quando usá-las pode ser confuso: Embora a versatilidade das fatias os faça uma escolha mais apropriada na maioria das situações, existem instâncias específicas em que arrays podem otimizar o desempenho do seu programa.
Este artigo abordará detalhadamente arrays (matrizes) e slices (fatias), fornecendo-lhe com a informação necessária para fazer a escolha apropriada entre esses tipos de dados. Além disso, você revisará as maneiras mais comuns de declarar e trabalhar com arrays e slices. O tutorial primeiro fornecerá uma descrição de arrays e como manipulá-los, seguido de uma explicação de slices e de como eles diferem.
Matrizes
Matrizes são estruturas de dados de coleção com um número fixo de elementos. Como o tamanho de uma matriz é estático, a estrutura de dados precisa alocar memória apenas uma vez, ao contrário de uma estrutura de dados de tamanho variável que precisa alocar memória de forma dinâmica para que ela possa tornar-se maior ou menor no futuro. Embora o tamanho fixo das matrizes possa torná-las um pouco rígidas para trabalhar, a alocação de memória apenas uma vez pode aumentar a velocidade e o desempenho do seu programa. Devido a isso, os desenvolvedores normalmente usam matrizes quando otimizam programas em casos onde a estrutura de dados nunca precisará de um número variável de elementos.
Definição de Matriz
Vetores são definidos declarando o tamanho do vetor dentro de parêntesis [ ]
, seguido do tipo de dado dos elementos. Um vetor em Go deve ter todos os seus elementos com o mesmo tipo de dado. Após o tipo de dado, você pode declarar os valores individuais dos elementos do vetor dentro de chaves { }
.
O seguinte é o esquema geral para declarar um vetor:
Nota: É importante lembrar que cada declaração de um novo vetor cria um tipo distinto. Portanto, apesar de [2]int
e [3]int
ambos ter elementos inteiros, suas diferentes longevidades tornam seus tipos de dados incompatíveis.
Se você não declarar os valores dos elementos do vetor, o padrão é zero, o que significa que os elementos do vetor estarão vazios. Para inteiros, isso é representado por 0
, e para strings, isso é representado por uma string vazia.
Por exemplo, o vetor numbers
a seguir tem três elementos inteiros que ainda não possuem valores:
Se você imprimisse numbers
, você obteria a seguinte saída:
Output[0 0 0]
Se você quiser atribuir os valores dos elementos ao criar o vetor, coloque os valores dentro de chaves. Um vetor de strings com valores definidos parece assim:
Você pode armazenar um vetor em uma variável e imprimi-lo:
Executar um programa com as linhas anteriores lhe daria a seguinte saída:
Output[blue coral staghorn coral pillar coral elkhorn coral]
Observe que não há delimitação entre os elementos do array quando é impresso, fazendo com que seja difícil identificar onde um elemento termina e outro começa. Devido a isso, às vezes é útil usar a função fmt.Printf
em vez disso, que pode formatar strings antes de as imprimir na tela. Fornecer o verbo %q
com este comando para instruir a função a colocar aspas em volta dos valores:
Isso resultará no seguinte:
Output["blue coral" "staghorn coral" "pillar coral" "elkhorn coral"]
Agora cada item está com aspas. O verbo \n
instrui ao formador a adicionar um retorno de linha no fim.
Com uma ideia geral de como declarar arrays e do que consistem, você pode agora passar para aprender como especificar elementos the array com um número de índice.
Indexação de arrays (e slices)
Cada elemento the array (e também o slice) pode ser chamado individualmente por meio de indexação. Cada elemento corresponde a um número de índice, que é um valor do tipo int
começando do número de índice 0
e contando para cima.
Nós usaremos um array nos exemplos seguintes, mas você poderia usar um slice também, já que eles são idênticos em como indexamos os dois.
Para o array coral
, a decomposição do índice parece assim:
“blue coral” | “staghorn coral” | “pillar coral” | “elkhorn coral” |
---|---|---|---|
0 | 1 | 2 | 3 |
O primeiro elemento é a string "blue coral"
, que começa no índice 0
e a fatia termina no índice 3
com o elemento "elkhorn coral"
.
Porque cada elemento de uma fatia ou um array tem um número de índice correspondente, podemos acessar e manipular-los na mesma maneira que outros tipos de dados seqüenciais.
Agora poderemos chamar um elemento discreto da fatia ou do array referenciando seu número de índice:
Outputstaghorn coral
Os números de índice para esta fatia variam de 0-3
, como mostrado na tabela anterior. Então para chamar qualquer elemento individualmente, você terá que referir-se ao número de índice desta maneira:
Se você chama o array coral
com um índice numérico maior que 3
, será fora de alcance pois não será válido:
Outputpanic: runtime error: index out of range
Quando indexar um array ou uma fatia, você deve sempre usar um número positivo. Contrariamente às linguagens em que você pode indexar para trás com um número negativo, fazer isso em Go resultará em um erro:
Outputinvalid array index -1 (index must be non-negative)
Veja como concatenar elementos de strings dentro de arrays ou fatias com outras strings usando o operador +
:
OutputSammy loves blue coral
Concatenamos o elemento de string no índice número 0
com a string "Sammy loves "
.
Com números de índice que correspondem aos elementos dentro de um array ou uma fatia, podemos acessar cada elemento discretamente e trabalhar com esses elementos. A seguir, veremos como modificar um elemento em determinado índice.
Modificando Elementos
Nós podemos usar índices para mudar elementos dentro de um array ou slice atribuindo um valor diferente a um elemento com um índice numérico. Isso dá-nos maior controle sobre os dados em nossos slices e arrays, e permitirá que manipulemos elementos individuais de forma programática.
Se quisermos mudar o valor da string do elemento na posição de índice 1
do array coral
de "staghorn coral"
para "foliose coral"
, podemos fazer isso desta forma:
Agora, quando imprimirmos o coral
, o array será diferente:
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral"]
Agora que você sabe como manipular elementos individuais the array ou slice, vamos olhar para algumas funções que nos darão mais flexibilidade quando trabalhando com tipos de dados de coleção.
Contando Elementos com len()
No Go, len()
é uma função interna feita para ajudá-lo a trabalhar com arrays e slices. Assim como com strings, você pode calcular a quantidade de elementos em um array ou slice usando len()
e passando o array ou slice como um parâmetro.
Por exemplo, para descobrir quantos elementos há no array coral
, você usaria:
Se você imprimir o tamanho da array coral
, você receberá o seguinte saída:
Output4
Este é o tamanho da array 4
no tipo de dado int
, o que é correto porque a array coral
tem quatro itens:
Se você criar uma array de integers com mais elementos, você pode usar também a função len()
para isso:
Isso resultaria na seguinte saída:
Output13
Mesmo que estas amostras tenham um número relativamente pequeno de itens, a função len()
é especialmente útil quando você deseja determinar quantos elementos há em arrays muito grandes.
Agora, vamos explorar como adicionar um elemento à uma coleção de tipos de dados e mostrar como, devido ao tamanho fixo das arrays, adicionar esses tipos estáticos de dados irá resultar em um erro.
Adicionando Elementos com append()
append()
é um método incorporado em Go que adiciona elementos a uma coleção de tipos de dados. No entanto, este método não funcionará quando usado com uma array. Como mencionado antes, a principal maneira em que as arrays diferem das slices é que o tamanho de uma array não pode ser modificado. Isso significa que enquanto você pode alterar os valores dos elementos de uma array, você não pode fazer a array maior ou menor depois que ela foi definida.
Considere sua array coral
:
Vou adicionar o item "black coral"
à este array. Se tentasse usar a função append()
com o array escrevendo:
Você receberia um erro pois sua saída seria:
Outputfirst argument to append must be slice; have [4]string
Para corrigir isso, vamos aprender mais sobre o tipo de dados slice, como definir uma slice, e como converter um array para uma slice.
Slices
Uma slice é um tipo de dado em Go que é mutable, ou seja, alterável, uma seqüência ordenada de elementos. Como o tamanho de uma slice é variável, há muita flexibilidade quando trabalhando com coleções de dados; quando trabalhar com coleções de dados que podem expandir ou contrair no futuro, usar uma slice garante que seu código não encontrará erros ao tentar manipular a quantidade de elementos da coleção. Na maioria dos casos, esta mutabilidade é digna do peso que pode requerir algumas vezes a reallocação de memória por parte das slice, quando comparadas a arrays. Quando você precisa armazenar muitos elementos ou iterar sobre elementos e você deseja facilmente modificar esses elementos, provavelmente voulez trabalhar com o tipo de dados slice. Quando você precisa de muitos elementos ou iterar sobre elementos e você deseja facilmente modificar esses elementos, provavelmente irá trabalhar com o tipo de dados slice.
Definindo uma Slice
Os segmentos são definidos declarando o tipo de dados precedido por um conjunto vazio de parênteses quadrados ([]
) e uma lista de elementos entre chaves ({}
). Você perceberá que, ao contrário de arrays que requerem um int
entre os parênteses para declarar uma especificação de tamanho, uma fatia não tem nada entre os parênteses, representando sua variável de tamanho.
Vamos criar uma fatia que contenha elementos do tipo de dado string:
Quando você imprimir a fatia, você verá os elementos que estão na fatia:
Isso irá resultar no seguinte:
Output["shark" "cuttlefish" "squid" "mantis shrimp" "anemone"]
Se você quiser criar uma fatia com uma determinada quantidade de elementos sem preencher os elementos da coleção ainda, você pode usar a função predefinida make()
:
Se você imprimisse esta fatia, você receberia:
Output["" "" ""]
Se você quiser alocar memória para uma capacidade certa, você pode passar um terceiro argumento para make()
:
Isso faria uma fatia zerada com um tamanho de 3
e uma capacidade pré-alocada de 5
elementos.
Agora você sabem como declarar uma fatia. No entanto, isso ainda não resolve o erro que tivemos com a matriz coral
anteriormente. Para usar a função append()
com coral
, primeiro você deve aprender como cortar seções de um array.
Cortando Matrizes em Fatias
Usando números de índice para determinar os pontos iniciais e finais, você pode chamar uma seção subjacente de valores dentro de um array. Isso é chamado de slicing o array, e você pode fazer isso criando um rango de índices separados por colones, na forma de [primeiro_índice:segundo_índice]
. É importante notar que, no entanto, quando cortar um array, o resultado é uma fatia, não um array.
Vamos dizer que você gostaria de imprimir apenas os itens do meio do array coral
, sem os primeiro e último elemento. Você pode fazer isso criando uma fatia começando em índice 1
e terminando justo antes do índice 3
:
Executar um programa com esta linha irá mostrar o seguinte:
Output[foliose coral pillar coral]
Quando você cria uma fatia, como em [1:3]
, o primeiro número é onde a fatia começa (inclusiva), e o segundo número é a soma do primeiro número com o total de elementos que você deseja retornar:
array[starting_index : (starting_index + length_of_slice)]
Neste caso, você chamou o segundo elemento (ou índice 1) como ponto inicial e chamou dois elementos em total. Este é como a calculação parece:
array[1 : (1 + 2)]
Que é como chegou à notação:
Se você quiser definir o início ou o fim do array como um ponto inicial ou final da fatia, você pode omitar um dos números na sintaxe array[primeiro_índice:segundo_índice]
. Por exemplo, se você quiser imprimir os três primeiros itens do array coral
— que seriam "blue coral"
, "foliose coral"
, e "pillar coral"
— você pode digitar:
Isso irá imprimir:
Output[blue coral foliose coral pillar coral]
Este comando imprimiu o início do array, parando justo antes do índice 3
.
Para incluir todos os itens no final de um array, você usaria a sintaxe inversa:
Isso iria dar a seguinte fatia:
Output[foliose coral pillar coral elkhorn coral]
Esta seção discutiu sobre chamar partes individuais de um array usando fatias. A próxima, você aprenderá como usar a fatiagem para convertir entradas inteiras em fatias.
Converter um Array para uma Fatia
Se você criou um array e decidir que precisa dele ter uma variável de tamanho, você pode converter-lo para uma fatia. Para convertir um array numa fatia, use o processo de fatiagem que você aprendeu na etapa Fatiagem de arrays em fatias deste tutorial, mas desta vez selecione toda a fatia ignorando ambos os números que determinariam os pontos finais:
Atenção: não é possível convertir a variável coral
em uma fatia propriamente dita, pois uma vez definida uma variável em Go, seu tipo não pode ser alterado. Para contornar isto, você pode copiar todo conteudo do array para uma nova variável como uma fatia:
Se você imprimisse coralSlice
, você receberia o seguinte saída:
Output[blue coral foliose coral pillar coral elkhorn coral]
Agora, tentar adicionar o elemento black coral
como na seção de arrays, usando append()
com a nova convertida fatia:
Isso irá exibir a fatia com o elemento adicionado:
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral"]
Também podemos adicionar mais de um elemento em uma única declaração append()
:
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia"]
Para juntar dois slices juntos, você pode usar append()
, mas deve expandir o segundo argumento para adicionar usando a sintaxe de expansão ...
:
Output["blue coral" "foliose coral" "pillar coral" "elkhorn coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
Agora que você aprendera a adicionar um elemento à sua slice, vamos olhar para como remover um.
Remover um Elemento de uma Slice
Diferentemente de outras linguagens, o Go não fornece nenhuma função interna para remover um elemento de uma slice. Os itens devem ser removidos de uma slice pela divisão deles.
Para remover um elemento, você deve dividir os itens antes desse elemento, dividir os itens depois desse elemento, e então append desses dois novos slices juntos sem o elemento que você quis remover.
Se i
é a posição de índice do elemento a ser removido, então o formato desse processo seria o seguinte:
De coralSlice
, vamos remover o item "elkhorn coral"
. Este item está localizado na posição de índice 3
.
Output["blue coral" "foliose coral" "pillar coral" "black coral" "antipathes" "leptopsammia" "massive coral" "soft coral"]
Agora o elemento na posição de índice 3
, a string "elkhorn coral"
, já não está em nossa slice coralSlice
..
Nós também podem excluir um intervalo com o mesmo método. Dizendo-se que queremos remover não apenas o item "elkhorn coral"
, mas também "black coral"
e "antipathes"
. Podemos usar uma expressão de intervalo para conseguir isso:
Esta expressão irá remover os índices 3
, 4
, e 5
da fatia:
Output["blue coral" "foliose coral" "pillar coral" "leptopsammia" "massive coral" "soft coral"]
Agora que você sabe como adicionar e remover elementos de uma fatia, vamos ver como medir a quantidade de dados que uma fatia pode contener em qualquer momento.
Medindo a Capacidade de uma Fatia com o cap()
Como as fatias tem tamanho variável, o método len()
não é a melhor opção para determinar o tamanho deste tipo de dado. Ao invés disso, você pode usar a função cap()
para saber a capacidade de uma fatia. Isso mostrará quantos elementos a fatia pode ter, o que é determinado por quanto memória já foi alocada para a fatia.
Nota: Porque a lungura e a capacidade de um array sempre são iguais, o método cap()
não funcionará em arrays.
Uma aplicação comum para o cap()
é criar uma fatia com um número pré-definido de elementos e preencher esses elementos programaticamente. Isso evita possíveis alocações de memória desnecessárias que poderiam ocorrer se fossem usadas funções como append()
para adicionar elementos além do tamanho atualmente alocado.
Vamos considerar o cenário onde queremos criar uma lista de números, de 0
até 3
. Podemos usar append()
em um laço para fazer isso, ou podemos pre-alocar a slice primeiro e usar cap()
para percorrer e preencher os valores.
Primeiro, podemos olhar para o uso de append()
:
Output[0 1 2 3]
Neste exemplo, criamos uma slice, e então criamos um laço for
que iteraria quatro vezes. Cada iteração acrescentou o valor atual da variável de loop i
na indexação da slice numbers
. No entanto, isso poderia levar a alocações de memória desnecessárias que poderiam abrandar o seu programa. Quando adicionando a uma slice vazia, cada vez que você faz chamadas a append, o programa verifica a capacidade da slice. Se o elemento adicionado fizer com que a slice exceda esta capacidade, o programa alocará memória adicional para o mesmo. Isto cria sobrecarga adicional no seu programa e pode resultar em execução mais lenta.
Agora vamos preencher a slice sem usar append()
ao pre-alocar uma determinada longege/capacidade:
Output[0 1 2 3]
Neste exemplo, usamos make()
para criar uma slice e pre-alocou 4
elementos. Depois usamos a função cap()
no loop para iterar por cada elemento zerado, preenchendo cada um até alcançar a capacidade pre-alocada. Em cada loop, colocamos o valor atual da variável de loop i
no índice da slice numbers
.
Enquanto o exemplo de append()
e o de cap()
funcionam ambos de maneira equivalente, o exemplo de cap()
evita alocções de memória adicionais que seriam necessárias se fossem usadas funções como append()
.
Cortes Multidimensionais
Você pode também definir cortes que consistem em outros cortes, com cada lista envolvida dentro das listas maiores do parente. Estes chamados de cortes multidimensionales podem ser pensados como representando coordenadas multidimensionais; por exemplo, uma coleção de cinco cortes cada um dos quais tem seis elementos pode representar uma grida de duas dimensões com uma largura de cinco e uma altura de seis.
Vamos examinar o seguinte corte multidimensional:
Para acessar um elemento neste corte, você terá que usar vários índices, um para cada dimensão da construção:
Na linha de código anterior, primeiro identificamos o elemento na posição 0
do corte no índice 1
, então indicamos o elemento na posição 0
do corte no índice 0
. Isso irá retornar o seguinte:
OutputSammy
shark
Os valores de índice para os demais elementos individuais são:
Ao trabalhar com fatiamentos multidimensionais, é importante manter em mente que você precisará referir-se a mais de um número de índice para acessar elementos específicos dentro do fatiamento aninhado relevante.
Conclusão
Neste tutorial, você aprendeu os fundamentos do trabalho com arrays e fatiamentos em Go. Você passou por vários exercícios para mostrar como os arrays têm tamanho fixo, enquanto os fatiamentos têm tamanho variável, e descobriu como essa diferença afeta o uso situacional dessas estruturas de dados.
Para continuar estudando estruturas de dados em Go, consulte nosso artigo sobre Entendendo Mapas em Go, ou explore toda a série Como Programar em Go.
Source:
https://www.digitalocean.com/community/tutorials/understanding-arrays-and-slices-in-go