前書き

Goでは、_arrays_および_slices_はhttps://en.wikipedia.org/wiki/Data_structure [データ構造]であり、要素の順序付けられたシーケンスで構成されています。 これらのデータコレクションは、多くの関連する値を操作する場合に便利です。 これらは、一緒に属するデータをまとめ、コードを圧縮し、複数の値に対して同じメソッドと操作を一度に実行できるようにします。

Goの配列とスライスはどちらも要素の順序付きシーケンスですが、2つの間には大きな違いがあります。 Goの_array_はhttps://en.wikipedia.org/wiki/Data_structure[data structure]であり、作成時に定義された容量を持つ要素の順序付けられたシーケンスで構成されます。 配列にサイズが割り当てられると、サイズは変更できなくなります。 一方、_slice_は配列の可変長バージョンであり、これらのデータ構造を使用する開発者に柔軟性を提供します。 スライスは、他の言語では配列と考えるものを構成します。

これらの違いを考えると、一方を他方よりも使用する特定の状況があります。 Goを初めて使用する場合、それらをいつ使用するかを決定するのは混乱を招く可能性があります:スライスの汎用性はほとんどの状況でより適切な選択になりますが、配列がプログラムのパフォーマンスを最適化できる特定のインスタンスがあります。

この記事では、配列とスライスについて詳しく説明します。これらのデータ型を選択する際に適切な選択を行うために必要な情報を提供します。 さらに、配列とスライスの両方を宣言して操作する最も一般的な方法を確認します。 このチュートリアルでは、まず配列の説明と配列の操作方法を説明し、次にスライスとその違いについて説明します。

配列

配列は、要素数が設定されたコレクションデータ構造です。 配列のサイズは静的であるため、メモリ構造を動的に割り当てる必要がある可変長データ構造とは対照的に、データ構造は一度だけメモリを割り当てる必要があります。 配列の長さが固定されているため、作業が多少難しくなりますが、1回限りのメモリ割り当てにより、プログラムの速度とパフォーマンスが向上します。 このため、開発者は通常、データ構造が可変量の要素を必要としないインスタンスでプログラムを最適化するときに配列を使用します。

配列の定義

配列は、角括弧 `+ [] `で配列のサイズを宣言し、続いて要素のデータ型を宣言することで定義されます。 Goの配列は、すべての要素が同じhttps://www.digitalocean.com/community/tutorials/understanding-data-types-in-go[data type]である必要があります。 データ型の後、中括弧 ` {} +`で配列要素の個々の値を宣言できます。

以下は、配列を宣言するための一般的なスキーマです。

[]{}

配列の要素の値を宣言しない場合、デフォルトはゼロ値です。つまり、配列の要素は空になります。 整数の場合、これは「0」で表され、文字列の場合、空の文字列で表されます。

たとえば、次の配列 `+ numbers +`には、まだ値を持たない3つの整数要素があります。

var numbers [3]int

`+ numbers +`を印刷した場合、次の出力が表示されます。

Output[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)

上記の行でプログラムを実行すると、次の出力が得られます。

Output[blue coral staghorn coral pillar coral elkhorn coral]

印刷時に配列内の要素間に線引きがないため、1つの要素がどこで終わり、別の要素がどこから始まるかを見分けるのが難しくなっていることに注意してください。 このため、代わりに `+ fmt.Printf `関数を使用すると便利な場合があります。これは、画面に出力する前に文字列をフォーマットできます。 このコマンドで `%q +`動詞を提供して、値を引用符で囲むように関数に指示します。

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

これにより、次の結果が得られます。

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

これで、各アイテムが引用されます。 `+ \ n +`動詞は、最後に改行を追加するようフォーマッタに指示します。

配列の宣言方法とその構成の一般的な考え方を理解したら、次に、インデックス番号を使用して配列内の要素を指定する方法の学習に進むことができます。

インデックス配列(およびスライス)

配列内の各要素(およびスライス)は、インデックス作成によって個別に呼び出すことができます。 各要素はインデックス番号に対応します。インデックス番号は、インデックス番号「0」からカウントアップする「+ int +」値です。

次の例では配列を使用しますが、両方のインデックスを作成する方法が同じであるため、スライスも使用できます。

配列 `+ coral +`の場合、インデックスの内訳は次のようになります。

“blue coral” “staghorn coral” “pillar coral” “elkhorn coral”

0

1

2

3

最初の要素である文字列 `” blue coral “`はインデックス `+ 0 `で始まり、スライスは要素 `” elkhorn coral “`でインデックス ` 3 +`で終わります。

スライスまたは配列の各要素には対応するインデックス番号があるため、他のシーケンシャルデータ型と同じ方法でそれらにアクセスして操作できます。

これで、インデックス番号を参照して、スライスの個別の要素を呼び出すことができます。

fmt.Println(coral[1])
Outputstaghorn coral

前の表に示すように、このスライスのインデックス番号の範囲は「+ 0-3 +」です。 したがって、要素を個別に呼び出すには、次のようにインデックス番号を参照します。

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

「3」より大きいインデックス番号で配列「+ coral +」を呼び出すと、無効になるため範囲外になります。

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

配列またはスライスにインデックスを付けるときは、常に正の数を使用する必要があります。 負の数で逆方向にインデックス付けできる一部の言語とは異なり、Goでそれを行うとエラーになります。

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

`+`演算子を使用して、配列またはスライス内の文字列要素を他の文字列と連結できます。

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

インデックス番号 `+ 0 `の文字列要素を文字列 `” Sammy loves “+`と連結することができました。

配列またはスライス内の要素に対応するインデックス番号を使用して、各要素に個別にアクセスし、それらの要素を操作できます。 これを実証するために、特定のインデックスで要素を変更する方法を次に見ていきます。

要素の変更

インデックス付けされた要素に異なる値を設定することにより、インデックスを使用して配列またはスライス内の要素を変更できます。 これにより、スライスと配列内のデータをより細かく制御できるようになり、個々の要素をプログラムで操作できるようになります。

配列 `+ coral `のインデックス ` 1 `にある要素の文字列値を `” staghorn coral “`から `” foliose coral “+`に変更する場合は、次のようにします。 :

coral[1] = "foliose coral"

`+ coral +`を出力すると、配列が異なります:

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

配列またはスライスの個々の要素を操作する方法がわかったので、コレクションデータ型を操作する際の柔軟性を高めるいくつかの関数を見てみましょう。

`+ len()+`で要素を数える

Goでは、 `+ len()`は、配列とスライスの操作を支援するために作られた組み込み関数です。 文字列と同様に、 ` len()+`を使用して配列またはスライスをパラメーターとして渡すことで、配列またはスライスの長さを計算できます。

たとえば、 `+ coral +`配列に含まれる要素の数を調べるには、次を使用します。

len(coral)

配列 `+ coral +`の長さを出力すると、次の出力が表示されます。

Output4

これは、 `+ int `データ型の配列 ` 4 `の長さを示します。これは、配列 ` coral +`に4つの項目があるため正しいです:

coral := [4]string{"blue coral", "foliose 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))

これにより、次の出力が得られます。

Output13

これらの配列例には比較的少ない項目がありますが、 `+ len()+`関数は非常に大きな配列に含まれる要素の数を判断するときに特に役立ちます。

次に、コレクションデータ型に要素を追加する方法について説明し、配列の長さが固定されているため、これらの静的データ型を追加するとエラーが発生する方法を示します。

`+ append()+`で要素を追加する

`+ append()+`は、コレクションデータ型に要素を追加するGoの組み込みメソッドです。 ただし、このメソッドは配列で使用すると機能しません。 前述のように、配列がスライスと異なる主な方法は、配列のサイズを変更できないことです。 つまり、配列内の要素の値を変更することはできますが、定義後に配列を拡大または縮小することはできません。

あなたの `+ coral +`配列を考えてみましょう:

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

アイテム `” black coral “`をこの配列に追加するとします。 次のように入力して、配列で `+ append()+`関数を使用しようとした場合:

coral = append(coral, "black coral")

出力としてエラーが表示されます。

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

これを修正するために、スライスのデータ型、スライスの定義方法、配列からスライスへの変換方法について詳しく学びましょう。

スライス数

slice_はGoのデータ型で、_mutable、または要素の変更可能な順序付きシーケンスです。 スライスのサイズは可変であるため、スライスを使用するときの柔軟性がはるかに高くなります。将来拡張または縮小する必要があるデータコレクションを使用する場合、スライスを使用すると、コレクションの長さを操作しようとしたときにコードでエラーが発生しなくなります。 ほとんどの場合、この可変性は、配列と比較した場合にスライスが必要とするメモリ再割り当ての価値があります。 多くの要素を保存したり、要素を繰り返し処理する必要があり、それらの要素を簡単に変更できるようにしたい場合は、スライスデータ型を使用することをお勧めします。

スライスの定義

スライスは、空の角かっこセット( + [] +)と中かっこで囲まれた要素のリスト( + {} +)が前にあるデータ型を宣言することによって定義されます。 特定の長さを宣言するために角括弧の間に「+ int +」を必要とする配列とは対照的に、スライスは角括弧の間に何もなく、可変長を表します。

文字列データ型の要素を含むスライスを作成しましょう。

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

スライスを印刷すると、スライス内の要素が表示されます。

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

これにより、次の結果が得られます。

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

コレクションの要素にデータを追加せずに特定の長さのスライスを作成する場合は、組み込みの `+ make()+`関数を使用できます。

oceans := make([]string, 3)

このスライスを印刷すると、次のようになります:

Output["" "" ""]

特定の容量のメモリを事前に割り当てたい場合、3番目の引数を `+ make()+`に渡すことができます:

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

これにより、長さが「3」で事前に割り当てられた容量が「5」要素のゼロ化されたスライスが作成されます。

これで、スライスの宣言方法がわかりました。 ただし、これは以前の `+ coral `配列で発生したエラーをまだ解決していません。 ` coral `で ` append()+`関数を使用するには、最初に配列のセクションをスライスする方法を学ぶ必要があります。

配列をスライスにスライスする

インデックス番号を使用して開始点と終了点を決定することにより、配列内の値のサブセクションを呼び出すことができます。 これは配列の_スライシング_と呼ばれ、 `+ [:] +`の形式でコロンで区切られたインデックス番号の範囲を作成することでこれを行うことができます。 ただし、配列をスライスする場合、結果は配列ではなくスライスになります。

最初と最後の要素なしで、 `+ coral `配列の真ん中のアイテムだけを印刷したいとしましょう。 これを行うには、インデックス「+1」で始まり、インデックス「3」の直前で終わるスライスを作成します。

fmt.Println(coral[1:3])

この行でプログラムを実行すると、次の結果が得られます。

Output[foliose coral pillar coral]

`+ [1:3] +`のようにスライスを作成する場合、最初の数値はスライスの開始位置(包括的)であり、2番目の数値は最初の数値と目的の要素の総数の合計です取得:

array[starting_index : (starting_index + length_of_slice)]

この例では、2番目の要素(またはインデックス1)を開始点として呼び出し、合計2つの要素を呼び出しました。 計算は次のようになります。

array[1 : (1 + 2)]

この表記法に到達した方法は次のとおりです。

coral[1:3]

配列の開始点または終了点をスライスの開始点または終了点として設定する場合、 + array [:] +`構文のいずれかの数字を省略できます。 たとえば、配列 `+ coral +`の最初の3つの項目を印刷する場合、これは `+" blue coral "+" foliose coral "、および `” pillar coral “`になります。 -次のように入力して、これを行うことができます。

fmt.Println(coral[:3])

これは印刷されます:

Output[blue coral foliose coral pillar coral]

これにより、配列の先頭が出力され、インデックス「3」の直前で停止しました。

配列の最後にすべてのアイテムを含めるには、構文を逆にします。

fmt.Println(coral[1:])

これにより、次のスライスが得られます。

Output[foliose coral pillar coral elkhorn coral]

このセクションでは、サブセクションをスライスして配列の個々の部分を呼び出すことについて説明しました。 次に、スライスを使用して配列全体をスライスに変換する方法を学習します。

配列からスライスへの変換

配列を作成し、可変長にする必要があると判断した場合は、スライスに変換できます。 配列をスライスに変換するには、このチュートリアルの「スライスを配列にスライスする」ステップで学習したスライスプロセスを使用します。ただし、今回はエンドポイントを決定する両方のインデックス番号を省略してスライス全体を選択します。

coral[:]

変数 `+ coral +`をスライス自体に変換することはできません。変数がGoで定義されると、そのタイプは変更できないためです。 これを回避するには、配列の内容全体をスライスとして新しい変数にコピーします。

coralSlice := coral[:]

`+ coralSlice +`を印刷した場合、次の出力が表示されます。

Output[blue coral foliose coral pillar coral elkhorn coral]

次に、新しく変換されたスライスで `+ append()`を使用して、配列セクションのように ` black coral +`要素を追加してみてください:

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

これにより、要素が追加されたスライスが出力されます。

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

単一の `+ append()+`ステートメントに複数の要素を追加することもできます:

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

2つのスライスを結合するには、 `+ append()`を使用できますが、2番目の引数を展開して、 ` …​ +`展開構文を使用して追加する必要があります。

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"]

スライスに要素を追加する方法を学習したので、要素を削除する方法を見てみましょう。

スライスから要素を削除する

他の言語とは異なり、Goはスライスから要素を削除するための組み込み関数を提供しません。 スライスするアイテムをスライスから削除する必要があります。

要素を削除するには、その要素の前にアイテムをスライスし、その要素の後にアイテムをスライスし、削除する要素なしでこれらの2つの新しいスライスを一緒に追加する必要があります。

`+ i +`が削除される要素のインデックスである場合、このプロセスの形式は次のようになります。

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

`+ coralSlice `から、アイテム `” elkhorn coral “`を削除しましょう。 このアイテムは、インデックス位置「+3」にあります。

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"]

これで、インデックス位置 `+ 3 `の要素、文字列 `” elkhorn coral “`は、スライス ` coralSlice +`に含まれなくなりました。

同じ方法で範囲を削除することもできます。 アイテム `” elkhorn coral “`だけでなく、 `” black coral “`と `” antipathes “`も削除したいとします。 式で範囲を使用してこれを実現できます。

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)

このコードは、スライスからインデックス「3 +」、「 4+」、および「5」を取り出します。

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

スライスに要素を追加および削除する方法がわかったので、スライスがいつでも保持できるデータ量を測定する方法を見てみましょう。

`+ cap()+`を使用してスライスの容量を測定する

スライスは可変長なので、 `+ len()`メソッドはこのデータ型のサイズを決定する最適なオプションではありません。 代わりに、 ` cap()+`関数を使用してスライスの容量を知ることができます。 これにより、スライスが保持できる要素の数が表示されます。これは、スライスに既に割り当てられているメモリの量によって決まります。

`+ cap()`の一般的な使用法は、プリセットされた要素数でスライスを作成し、それらの要素をプログラムで埋めることです。 これにより、 ` append()+`を使用して現在割り当てられている容量を超えて要素を追加することで発生する可能性のある不要な割り当てを回避できます。

「0」から「3」までの数字のリストを作成するシナリオを考えてみましょう。 そのためには、ループで `+ append()`を使用するか、最初にスライスを事前に割り当て、 ` cap()+`を使用してループして値を埋めることができます。

最初に、 `+ append()+`を使用して見てみましょう。

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

この例では、スライスを作成してから、4回反復する「+ for 」ループを作成しました。 各反復は、ループ変数「 i 」の現在の値を「 numbers +」スライスのインデックスに追加しました。 ただし、これにより不必要なメモリ割り当てが発生し、プログラムの速度が低下する可能性があります。 空のスライスに追加する場合、appendを呼び出すたびに、プログラムはスライスの容量をチェックします。 追加された要素によってスライスがこの容量を超える場合、プログラムはそれを考慮して追加のメモリを割り当てます。 これにより、プログラムにオーバーヘッドが追加され、実行が遅くなる可能性があります。

次に、特定の長さ/容量を事前に割り当てることで、 `+ append()+`を使用せずにスライスを作成します。

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

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

この例では、 `+ make()`を使用してスライスを作成し、 ` 4 `要素を事前に割り当てました。 次に、ループで「 cap()」関数を使用して、ゼロ化された各要素を繰り返し処理し、事前に割り当てられた容量に達するまでそれぞれを埋めていきました。 各ループで、ループ変数「 i 」の現在の値を「 numbers +」スライスのインデックスに配置しました。

`+ append()`戦略と ` cap()`戦略はどちらも機能的には同等ですが、 ` cap()`の例では、 ` append()を使用して必要となる追加のメモリ割り当てを回避します+ `関数。

多次元スライスの構築

また、他のスライスで構成されるスライスを要素として定義することもできます。各スライスのリストは、親スライスの大きな括弧で囲まれます。 このようなスライスのコレクションは、_多次元スライス_と呼ばれます。 これらは、多次元座標を描いていると考えることができます。たとえば、長さがそれぞれ6要素の5つのスライスのコレクションは、水平の長さが5、垂直の高さが6の2次元グリッドを表すことができます。

次の多次元スライスを調べてみましょう。

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

このスライス内の要素にアクセスするには、コンストラクトの各次元に1つずつ、複数のインデックスを使用する必要があります。

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

上記のコードでは、最初にインデックス「1」のスライスのインデックス「0」の要素を特定し、次にインデックス「0」のスライスのインデックス「0」の要素を示します。 これにより、次の結果が得られます。

OutputSammy
shark

以下は、個々の要素の残りのインデックス値です。

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"

多次元スライスを使用する場合、関連するネストされたスライス内の特定の要素にアクセスするには、複数のインデックス番号を参照する必要があることに注意してください。

結論

このチュートリアルでは、Goで配列とスライスを操作する基礎を学びました。 スライスの長さが可変であるのに対し、配列の長さが固定されている方法を実演するために複数の演習を行い、この違いがこれらのデータ構造の使用状況にどのように影響するかを発見しました。

Goでデータ構造の研究を続けるには、https://www.digitalocean.com/community/tutorials/understanding-maps-in-go [Goのマップについて]の記事をご覧になるか、https:// www全体をご覧ください。 .digitalocean.com / community / tutorial_series / how-to-code-in-go [How in Code in Go]シリーズ。