3.Go言語のデータ型
前回の記事ではGo言語の配列型を紹介しましたが、今回はGo言語のスライス型を紹介します。
3.3スライス
スライスは、アレイのパッケージングの一形態と見なすことができます。スライスでラップされた配列は、スライスの基になる配列と呼ばれます。スライスは、基になる配列内の連続セグメントの記述子です。
3.3.1型表記
要素タイプがTのスライスタイプの場合、そのタイプリテラルは次のとおりです。
[]T
長さがスライスタイプの一部ではないことがわかります(つまり、スライスタイプを表すタイプリテラルには表示されません)。さらに、スライスの長さは可変です。同じタイプのスライス値は、長さが異なる場合があります。
スライス型宣言の要素型は、任意の有効なGo言語データ型にすることもできます。例えば:
[]rune
上記は、要素タイプがルーンであるスライスタイプを示すために使用されます。
スライスタイプの要素タイプとして匿名構造タイプを使用することもできます。例えば:
[] struct {name, department string}
3.3.2値の表記
配列と同様に、これも複合リテラルの一種です。次に例を示します。
[]string{"Go", "Python", "Java", "C", "C++", "PHP"}
スライス値が属するタイプの長さについての規定はありません。次のスライスは合法です。
[]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}
上記は、次の複合リテラルと同等です。
[]string{0: "", 1: "", 2: "Swift", 3: "Java", 4: "C", 5: "C++", 6: "PHP", 7: "", 8: "Go"}
3.3.3プロパティと基本操作
スライスタイプのゼロ値はnilです。初期化前のスライスタイプ変数の値はnilです。
スライスタイプの長さについての記述はありませんが、値には長さがあり、それはそれらに含まれる要素値の実際の数に反映されます。組み込み関数lenを使用して、スライス値の長さを取得できます。例えば:
len([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})
上記の計算の結果は9です。このスライス値には、実際には6つの明示的に指定された文字列タイプ値と3つの入力された文字列タイプゼロ値 ""が含まれています。
注:組み込み関数lenをスライスタイプのゼロ値(つまりnil)に適用すると、結果は0になります。
-
スライス値の基本的な実装:
- スライス値は常に配列値への参照を保持します。スライス値が初期化されると、要素値を含む配列値に関連付けられます。この配列値は、彼のスライス値を参照する基になる配列と呼ばれます。
複数のスライス値が同じ基になる配列を共有する場合があります。たとえば、1つのスライス値が複数のスライス値にコピーされる場合、または特定の連続セグメントに対して新しいスライス値がスライスされる場合、これらのスライス値はすべて同じ基になる配列を参照します。スライス値の要素値の変更は、基本的に、基になる配列の対応する要素の変更です。逆に言えば、基になる要素としての配列値の要素値への変更は、基になる配列を参照し、要素値を含むすべてのスライス値にも反映されます。
長さに加えて、スライス値には非常に重要な属性である容量があります。スライス値の容量は、それが保持する基になる配列の長さに関連しています。内蔵のファンクションキャップで入手できます。例えば:
cap([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})
スライス値の容量は9で、これはその長さと同じです。これは特別な場合ですが、多くの場合そうではないので、ゆっくり聞いてみましょう。
-
スライス値の基礎となるデータ構造:
-
スライス値の基になるデータ構造には、基になる配列を指すポインター型の値、スライスの長さを表すint型の値、およびスライスの容量を表すint型の値が含まれます。
スライス式を使用して、配列値またはスライス値から連続セグメントを「切り取り」、新しいスライス値を生成できます。例えば:
array1 := [...]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}
slice1 := array1[:4]
以下に示すように、変数slice1の値の基になる配列は、実際には変数array1の値です。
上記の説明の後、スライスの容量は、その下にある配列の長さであると考えるかもしれません。ただし、そうではありません。ここでは、別のスライス値を作成します。例えば:
slice2 := array1[3:]
以下に示すように、変数slice2の値の基になる配列は、変数array1の値でもあります。
上に示したように、slice2の値の容量は、array1の値の長さと同じではありません。実際、スライス値の容量は、基になる配列の最後の要素値へのポインターが指す要素値からのカウント値です。スライス値の容量の意味は、アクセスできる現在の基になる配列内の要素値の最大数です。
スライス値を展開して、より多くの基になる配列要素を表示できます。ただし、再スライスによってウィンドウを直接拡張することはできません。たとえば、上記の元のslice1値に対して次の操作を実行します。
slice1[4]
これにより、実行時のパニックが発生します。これは、インデックス値がスライス値の現在の長さを超えているためです。これは許可されていません。拡張する正しい方法は次のとおりです。
slice1 = slice1[:cap(slice1)]
スライス1を最大に再スライスすることで、最下位レベルの配列要素の値を確認できます。このとき、slice1の値の長さはその容量と同じです。
注:スライス値の容量は固定されています。つまり、表示できる基になる配列要素の最大数は固定されています。
スライス値は、その容量を超えて拡張することはできません。次に例を示します。
slice1 = slice1[:cap(slice1)+1]//超出slice1容量的范围,这样会引起一个运行时恐慌
スライス値は、インデックスが増加する方向にのみ拡張できます。例えば:
slice2 = slice2[-2:] //这会引起一个运行时恐慌。另外,切片值不允许由负整数字面量代表。
追加関数を使用して、最初にslice1の値を拡張します。
slice1 = append(slice1, "Ruby", "Erlang")
このステートメントを実行した後、スライスタイプ変数slice1の値とその基になる配列の状態(配列変数array1の値)は次のようになります。
スライス1の値の長さが元の4から6に増加していることがわかります。これは、その容量と同じです。ただし、この値の長さはその容量を超えていないため、新しい基になる配列を作成する必要はありません。
元のslice1の値は次のとおりです。
[]string{"Go", "Python", "Java", "C"}
スライス1の現在の値は次のとおりです。
[]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}
array1の元の値は次のとおりです。
[6]string{"Go", "Python", "Java", "C", "C++", "PHP"}
array1の現在の値は次のとおりです。
[6]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}
次のように現在のslice1を拡張します。
slice1 = append(slice1, "Lisp")
このステートメントを実行した後、変数slice1の値の長さがその容量を超えています。このとき、新しい配列値が作成され、初期化されます。この新しい配列値は、append関数で新しく作成されたスライス値の一番下の配列として使用され、元のスライス値のすべての要素値と拡張コンテンツとしてのすべての要素値が含まれます。新しいスライス値のポインターは、基になる配列の最初の要素値を指し、その長さと容量は、基になる配列の長さと同じです。最後に、この新しいスライス値が変数slice1に割り当てられます。
追加機能を使用して、同じ要素タイプを持つ2つのスライス値を接続できます。例えば:
slice1 = append(slice1, slice...)
もちろん、配列値を2番目のパラメーターとしてappend関数に渡すこともできます。
スライスタイプ変数の値がゼロnilであっても、長さが0のスライス値と見なされます。例えば:
slice2 = nil
slice2 = append(slice2, slice1...)
または次のように:
var slice4 []string
slice4 = append(slice4, slice...)
上記の最初のステートメントは、変数を宣言する(初期化を含まない)ために使用されます。キーワードvarで始まり、変数の名前とタイプが続きます。初期化されていないスライス変数の値はnilです。
3.3.4スライスの複雑な使用法
スライス式に3番目のインデックスを追加します-----容量の上限インデックス。指定した場合、スライス式の評価結果のスライス値の容量は、スライス式の操作オブジェクトの容量と式の要素の下限インデックスの容量の差ではなく、容量の差になります。要素の境界インデックスと下限インデックスの間。
容量の上限インデックスを指定する目的は、新しいスライス値の容量を減らすことです。これにより、より柔軟なデータ分離戦略が可能になります。
var array2 [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice5 := array2[2:6]
上記のように、インデックス値が[2,6)の範囲内にあるarray2の要素値に直接アクセスして変更できます。
slice5 = slice5[:cap(slice5)]
上記のようにスライスした後、array2の値のインデックス値が[2,10)の範囲にある要素の値にアクセスして変更できます。
スライス5の値がデータキャリアとして別のプログラムに渡される場合、このプログラムはarray2の値の一部の要素の値を自由に変更できます。これは、プログラムの実装の詳細の一部を公開し、プログラムの内部状態を間接的に変更できるメソッドを明らかにすることと同じです。これは、多くの場合、私たちが望んでいることではありません。
スライス5が次のように宣言されている場合:
slice5 := array2[2:6:8]
このように、slice5の所有者は、array2の値の[2,8)の範囲内にあるインデックス値を持つ要素の値にのみアクセスして変更できます。
slice5 = slice5[:cap(slice5)]
スライス5が最大に拡張されても、対応するインデックス値がarray2の値の8以上である要素にはアクセスできません。このとき、slice5の値の容量は6(容量の上限インデックスと要素の下限インデックスの差)です。スライス操作の場合、操作対象の容量は克服できない限界です。スライス5の値は、基になる配列(array2の値)の「アクセス権」を制限します。
スライス5の値を超える拡張がその容量を超える場合:
slice5 = append(slice5, []int{10, 11, 12, 13, 14, 15}…)
次に、元の基になる配列が置き換えられます。また、slice5を介して元の基になる配列の要素値にアクセスして変更する方法を完全に遮断します。
スライス式の3つのインデックスの制限:スライス式で容量の上限インデックスが指定されている場合、要素の上限インデックスは省略できません。ただし、この場合、要素の下限インデックスは省略できます。例えば:
slice5[:3:5]//合法的切片表达式
slice5[0::5]//非法的切片表达式,会造成一个编译错误
スライス値の要素をバッチコピーします
sliceA := []string{"Notepad", "UltraEdit", "Eclipse"}
sliceB := []string{"Vim", "Emacs", "LiteIDE", "IDEA"}
Go言語の組み込み関数copyを使用して、変数sliceBの値の要素をsliceAの値にコピーします。例えば:
n1 := copy(sliceA,sliceB)
組み込み関数copyの機能は、ソーススライス値(2番目のパラメーター値)の要素値をターゲットスライス値(最初のパラメーター値)にコピーし、コピーされた要素値の数を返すことです。コピー関数の2つのパラメーターの要素タイプは同じである必要があり、実際にコピーする要素値の数は、短い方のスライス値の長さに等しくなります。
変数n1の値は3であり、変数sliceAの値は次のように変更されます。
[]string{"Vim", "Emacs", "LiteIDE"}