研究は、配列またはスライスは違いはありません指摘行きます

前回の記事では詳細Go指摘し、言語の基礎をGoと文法の詳細に関するより集中違い、他の主流のプログラミング言語、私は少しのメモリで簡単に既存のプログラミング言語を切り替えることができると信じているGoプログラミング言語の慣習スイッチは特に滑らかではなく、試行錯誤の練習より特に多くないかもしれませんが、常にゆっくりと感じることができるGo言語の美しさを!

研究ではGo、組み込みのコンテナの前に、同じ、我々は簡単に見直しGo、新たな温度の知識と、基本的な言語を教師としての役割を果たすことができます!

知識のレビューセクション

詳細については、内部の見にマイクロチャネル公共番号[スノードリームインテクノロジ]を喜ば研究はどのような基本的な文法の特定の懸念指摘行く記事を、便利、簡単に転送聖歌を感じます!

ビルトインタイプ種

  • bool

オプションのブール、true|falseゼロ初期のデフォルト値false

  • (u)int(u)int8(u)int16(u)int32(u)int64uintptr

2^0=12^1=2バイト整数の長さ、および符号付き整数、符号なし整数を含むポインタ型の種類を、デフォルト値はゼロに初期化されます2^2=4uintptr0

  • byte(uint8)rune(int32)string

byteこれは、最も基本的であるバイトのタイプがあるuint8の種類別名、及びruneある文字の種類、である別名。最も一般的なタイプの文字列は、それを導入しているべきではないのですか?Goint32string

  • float32float64complex64complex128

のみfloatfloat型、無doubleバイト単位で同じ長さを区別するタイプは、complex64それが複合型の実部と虚部でありfloat32、従って書き込み、製複合型complex64このフォーム。

特長は、ビルトインタイプ

  • のみ表示変換型の変換は、暗黙的型変換のno形式はありません

変数ではなく、異なる種類の間で自動的に暗黙の型変換Go型変換は、唯一の必須言語、できるだけ表示変換です。

  • ポインタの種類を提供する、しかしながら、ポインタ自体は、コンピュータの任意のフォームを行うことができません。

ポインタ型の変数を計算することはできませんが、メモリ・アドレスを変更するためにリダイレクトすることができます。

  • 変数の宣言は、ゼロのデフォルト値で初期化された後、ゼロ値変数のタイプに特異的に依存

int変数のゼロ値型が初期化され0stringゼロ値の型は、空の文字列に初期化されていませんnil

基本的な演算子

  • ノー算術演算子++i--i

のみi++、そしてi--このインクリメント演算子は、もはや2つの方法の違いを心配する必要はありません!

  • 比較演算子は、==平等のための配列を比較することができます

ときに、2つの配列の配列の長さおよび寸法の値は、二つの配列を比較することができ、場合とまったく同じ順序は、結果としてtrue、それ以外の場合ですfalse

  • ビット演算子ビット単位new演算子をクリア &^

他の主流のプログラミング言語は、そのような作業はないが、コマンドでも同様の機能を組み合わせることによって達成することができ、それが提供するので、ビットは、オペレータによりクリアされ、もはや自分自身の組み合わせを使用して実施する必要がありません!

フロー制御文

  • if 条件式は、変数の割り当てのための括弧とサポートを必要としません。

ロジックに従って、一時変数と変数を定義し、その後、さまざまな状況に応じた分類プロセスは、Go一時的な変数の取り扱い、直接的に強化された条件式は、このような状況は、将来的には非常に一般的になります!

  • if 条件式のスコープで定義された変数は、現在の文ブロックに限定されています

条件式で定義された変数は、処理ロジックの異なる分岐を容易にすることで、それが一時的な変数であるため、現在、if文のブロックを使用することができない、それは理解することが可能となります。

  • switch声明ではできないbreak、しない限りfallthrough

switch複数の文case末尾にはできませんbreak、システムが自動的になりますbreak処理します。

  • switch 条件式が整数定数に限定されず、又は

そして、他の主流のプログラミング言語に比べて、Go言語switchの条件式には、より強力なだけでなく、よりリラックスしたタイプです。

  • switch条件式は、ステアリング分岐ロジックを省略することができるcase言語。

省略switch条件式、複数のcase言語の分岐プロセス制御、多機能の効果if elseと同じです。

  • 省略switchそれぞれについて、条件式のcase条件は、カンマで区切って複数の条件を有していてもよいです。

swicth文は、本質的に異なる条件に応じて、それぞれ対応するフロー制御であるcase条件式は、複数をサポートするだけでなく、プロセス制御の能力を高めます。

  • for 条件式サイクルは、括弧、および循環の無い他のフォームを必要としません。

Go語学だけforサイクル、無while循環の他の形。

  • for ループ終了条件式の初期条件、および省略したりしながら自己増力を省略することができます

条件式を達成するために省略することができる後のwhileサイクルの効果を、すべての省略、エンドレスループです。

機能とパラメータの受け渡し

  • 関数名に応じて、関数宣言は、参照に、参照配列は定義され、複数の戻り値をサポートしています

変数定義や関数定義、かどうかはGo、常に私は、入力と出力のために考えることができれば、の反対に他の主流のプログラミング言語この定義は実際には非常に正当な方法であることがわかります。

  • 関数は、複数の戻り値が返り値に名前を付けることができます持っていますが、呼び出し側の面で違いはありません

あなたはすぐに意味を知られているおなじみの関数宣言の名前を確認するための呼び出しを容易にするために、関数の戻り複数の値の変数名を持っているかもしれませんが、呼び出し側は名前の結果に応じて、戻り値を受け取るために呼び出す必要はありません。

  • 参照機能は複雑な概念必要なパラメータ、オプションのパラメータではありませんに、唯一の変数引数リストをサポートしています

パラメータリストや他の主流のプログラミング言語変数、引数への最後の1でなければなりません。

  • 専用伝送値関数に引数を渡す、何ら言及は、すなわち、すべての変数は、コピーする必要があり、送信されません

唯一のパラメータの値が渡された、よりシンプルなロジックを渡されますが、複雑な状況に対処するポインタ参照を渡すの効果を達成するために渡すことができます。

何に構築されているコンテナ

投稿Go言語の基本的な文法を、私たちは、コンテナの担い手である知識の変数の型を学び続けるようになりました。

コンテナの配列は、最も基本的な基盤となる変数のクラスをされたキャリング、アレイパッケージを頼ることができる容器底部のほとんどのシニアは、我々は最初のを見てGo違いは何ですか配列?

配列とスライス

  • 配列を宣言して初期化

配列の独特の機能を使用すると、配列の長さも使用することができます指定されていないとき、連続したメモリの声明特定の長さのセットの長さは、配列を指定する必要があり、配列を宣言する際には、当然のことながら、初期化することができると同時に、である...私たちは、配列の長さを決定するのを助けるために、コンパイラの構文を。

func TestArray(t *testing.T) {
    var arr1 [3]int
    arr2 := [5]int{1, 2, 3, 4, 5}
    arr3 := [...]int{2, 4, 6, 8, 10}

    // [0 0 0] [1 2 3 4 5] [2 4 6 8 10]
    t.Log(arr1, arr2, arr3)

    var grid [3][4]int

    // [[0 0 0 0] [0 0 0 0] [0 0 0 0]]
    t.Log(grid)
}

[3]int指定された配列の長さ3、要素タイプはint、もちろん、直接宣言を割り当てることができる[5]int{1, 2, 3, 4, 5}指定された配列の長さを気にしている場合、それはすることができ、[...]int{2, 4, 6, 8, 10}発現します。

  • そして、配列アクセスの要素をトラバース

最も一般的なforループトラバースはアレイの指標に応じてアクセスされ、range arr方法は、トラバーサルを簡単にする便利な方法を提供します。

func TestArrayTraverse(t *testing.T) {
    arr := [...]int{2, 4, 6, 8, 10}

    for i := 0; i < len(arr); i++ {
        t.Log(arr[i])
    }

    for i := range arr {
        t.Log(arr[i])
    }

    for i, v := range arr {
        t.Log(i, v)
    }

    for _, v := range arr {
        t.Log(v)
    }
}

range arr唯一のインデックスエントリを気にし、インデックスの値を気にしないならば、あなたが使用することができ、インデックス値とインデックス・エントリに戻ることができ_プレースホルダをインデックス値が無視される表しインデックスのみの値を心配している場合、あなたはインデックス・エントリを書き込むことができません。これは、処理ロジックの関数であり、マルチ順次戻り値を受信し、未使用の変数が表示されないこと。

  • アレイは、値型を比較す​​ることが可能です

アレイは、アレイ内の同じ緯度と同じ数の要素が、この点でのコンテンツの前にも簡単にリコール再びここで強調されている、比較することができるように、この及び他の主流のプログラミング言語は、異なっていて、値型です。

func printArray(arr [5]int) {
    arr[0] = 666
    for i, v := range arr {
        fmt.Println(i, v)
    }
}

func TestPrintArray(t *testing.T) {
    var arr1 [3]int
    arr2 := [5]int{1, 2, 3, 4, 5}
    arr3 := [...]int{2, 4, 6, 8, 10}

    // [0 0 0] [1 2 3 4 5] [2 4 6 8 10]
    t.Log(arr1, arr2, arr3)

    // cannot use arr1 (type [3]int) as type [5]int in argument to printArray
    //printArray(arr1)

    fmt.Println("printArray(arr2)")
    printArray(arr2)

    fmt.Println("printArray(arr3)")
    printArray(arr3)

    // [1 2 3 4 5] [2 4 6 8 10]
    t.Log(arr2, arr3)
}

渡されたパラメータは値渡しされるので、そのためprintArrayの関数が渡された関数の呼び出し元の外部値を変更することはできませんあなたが機能するようにしたい場合は、printArray内部の変化を介してコンテンツ配信の配列のポインタを介して達成することができるが、より簡単な方法は何ですか?

したいprintArrayByPointer場所に慣れがない場合は、方法によって機能、配列ポインタの内部パラメータ配列を変更するには、レビュー記事を見るために見ることができます。

func printArrayByPointer(arr *[5]int) {
    arr[0] = 666
    for i, v := range arr {
        fmt.Println(i, v)
    }
}

func TestPrintArrayByPointer(t *testing.T) {
    var arr1 [3]int
    arr2 := [5]int{1, 2, 3, 4, 5}
    arr3 := [...]int{2, 4, 6, 8, 10}

    // [0 0 0] [1 2 3 4 5] [2 4 6 8 10]
    t.Log(arr1, arr2, arr3)

    fmt.Println("printArrayByPointer(arr2)")
    printArrayByPointer(&arr2)

    fmt.Println("printArrayByPointer(arr3)")
    printArrayByPointer(&arr3)

    // [666 2 3 4 5] [666 4 6 8 10]
    t.Log(arr2, arr3)
}

配列の要素は、加えて、ポインタの配列を渡すことによって達成され得る変更、Go言語が近い配列の相対的存在であるslice、すなわちスライス、それは同様の効果を得ることができます。

  • 宣言と初期化をスライス

配列が指定されたときにスライス及びアレイは、最終的なスライスは、実際の配列を作成していない配列の長さが作成されていない場合、非常に類似しています。

func TestSliceInit(t *testing.T) {
    var s1 [5]int
    // [0 0 0 0 0]
    t.Log(s1)

    var s2 []int
    // []
    t.Log(s2,len(s2))
}

[]int長さが指定されていない、スライスが作成され、この時間は、ゼロ初期のデフォルト値はnil空の配列ではありません!

同様に、あなたは配列を宣言して初期化することができ、スライスすることができ、構文は非常に似て、少し注意がそれはそれの配列だと思いました!

func TestSliceInitValue(t *testing.T) {
    var s1 = [5]int{1, 3, 5, 7, 9}
    // [1 3 5 7 9]
    t.Log(s1)

    var s2 = []int{1, 3, 5, 7, 9}
    // [1 3 5 7 9]
    t.Log(s2)
}

ただ、指定されていない[]長さを、最終的な結果は、スライス、本当にまぶしいなるために作成されました!

これと同様の配列とスライスは、人々は2間の日陰の取引は、実際には配列からのスライスを得ることができるものと疑われている、以下の例があります?:

func TestSliceFromArray(t *testing.T) {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    // arr =  [0 1 2 3 4 5 6 7 8 9]
    t.Log("arr = ", arr)

    // arr[2:6] =  [2 3 4 5]
    t.Log("arr[2:6] = ", arr[2:6])
    // arr[:6] =  [0 1 2 3 4 5]
    t.Log("arr[:6] = ", arr[:6])
    // arr[2:] =  [2 3 4 5 6 7 8 9]
    t.Log("arr[2:] = ", arr[2:])
    // arr[:] =  [0 1 2 3 4 5 6 7 8 9]
    t.Log("arr[:] = ", arr[:])
}

arr[start:end] アレイの一部の傍受によって得られた結果は、スライスのコンセプトは非常に鮮やかああで、スライスです!

そして、他の主流のプログラミング言語、[start:end]閉じられた左右の開区間で、スライスされた意味は非常に明確です:

開始インデックス無視しstartarr[:end]元の配列インデックスが終了するまで開始から代表end; 1前
無視終了インデックスendarr[ start:]インデックス起動元の配列から表現start最後のビットまで開始し、
両方が開始インデックスを無視して、インデックスの終了を無視しているがあまり一般的ではなく、元の配列の意味であることが、型がよ配列スライスではないことを覚えておいてください!

それはスライス操作や配列にそれをしない限り、我々はそれが配列のサイズにスライスし、スライスの相対的な配列に非常に似て知っていることは、唯一のではないでしょうか?

func updateSlice(s []int) {
    s[0] = 666
}

func TestUpdateSlice(t *testing.T) {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    // arr =  [0 1 2 3 4 5 6 7 8 9]
    t.Log("arr = ", arr)
    
    s1 := arr[2:6]
    // s1 =  [2 3 4 5]
    t.Log("s1 = ", s1)

    s2 := arr[:6]
    // s2 =  [0 1 2 3 4 5]
    t.Log("s2 = ", s2)

    updateSlice(s1)
    // s1 =  [666 3 4 5]
    t.Log("s1 = ", s1)
    // arr =  [0 1 666 3 4 5 6 7 8 9]
    t.Log("arr = ", arr)

    updateSlice(s2)
    // s2 =  [666 1 666 3 4 5]
    t.Log("s2 = ", s2)
    // arr =  [666 1 666 3 4 5 6 7 8 9]
    t.Log("arr = ", arr)
}

渡されたパラメータは、値のみが渡されるので、物事が実際にパラメータを渡すために、スライスを変更することができますが、これはああの配列を行っておりません!それはスライスポインタ内にある場合を除き、配列のポインタ型がない限り、スライスは?簡単に行うことができた、参照渡し方法はありません!

異なる、特定の性能に渡さスライスし、アレイの性能パラメータは、配列が配列パラメータ渡し、あなたは配列を渡したいだけの仕事のための配列ポインタを変更したいときに変更することはできませんし、配列変化のスライスを達成しています!

パラメータ値は一方向のみのパスを渡されるので、それはスライスポインタの内側に確かに存在することが推定される、パラメータ渡しはポインタが渡され、変更の内部関数は外部変数の機能に影響を与えることができます。

行く-コンテナに関するスライス-struct.png

slice内部変数、ポインタの3つの実装がありptr、数lenおよび容量capptr実データ格納アドレスにポイント。

このスライスの内部実装はイェジンハオを言及する価値特性の徴候を必要とするので、それは、本質的に、唯一のダイナミックアレイで、スイッチやアレイは密接にリンクされてなり、実際には、このデータは、静的配列の伸長の結果であり、しかしGo、言語が呼び出され、スライス

外部パラメータのスライス値を変更することができるように、スライスが動的配列であり、上記の問題を説明することは容易であり、伝送パラメータが渡された内部ポインタであるため、コピーポインタも渡された値が、実際の要素へのポインタは、結局、同じです。

配列がある程度比較することができ、動的配列スライスである、と比較することはできません?今、あなたの推測を検証するために、以下の試験方法をしましょう!

行く-コンテナに関するスライス-compare.png

不知道你有没有猜对呢?切片并不能进行比较,只能与 nil 进行判断.

  • 切片的添加和删除

数组是静态结构,数组的大小不能扩容或缩容,这种数据结构并不能满足元素个数不确定场景,因而才出现动态数组这种切片,接下来重点看下切片怎么添加或删除元素.

func printSlice(s []int) {
    fmt.Printf("s = %v, len(s) = %d, cap(s) = %d\n", s, len(s), cap(s))
}

func TestSliceAutoLonger(t *testing.T) {
    var s []int
    // []
    t.Log(s)

    for i := 0; i < 10; i++ {
        s = append(s, i)

        printSlice(s)
    }

    // [0 1 2 3 ...,98,99]
    t.Log(s)

    for i := 0; i < 10; i++ {
        s = s[1:]
        
        printSlice(s)
    }

    // [0 1 2 3 ...,98,99]
    t.Log(s)
}

添加元素 s = append(s, i) 需要扩容时,每次以 2 倍进行扩容,删除元素 s[1:] 时,递减缩容.

s = append(s, i) 向切片中添加元素并返回新切片,由于切片是动态数组,当切片内部的数组长度不够时会自动扩容以容纳新数组,扩容前后的内部数组会进行元素拷贝过程,所以 append 会返回新的地址,扩容后的地址并不是原来地址,所以需要用变量接收添加后的切片.

当不断进行切片重新截取时 s[1:] ,切片存储的元素开始缩减,个数递减,容量也递减.

行く-コンテナに関するスライス・アドオンと-delete.png

其实除了基于数组创建切片和直接创建切片的方式外,还存在第三种创建切片的方式,也是使用比较多的方式,那就是 make 函数.

func TestMakeSlice(t *testing.T) {
    s1 := make([]int,10)

    // s1 = [0 0 0 0 0 0 0 0 0 0], len(s1) = 10, cap(s1) = 10
    t.Logf("s1 = %v, len(s1) = %d, cap(s1) = %d", s1, len(s1), cap(s1))

    s2 := make([]int, 10, 32)

    // s2 = [0 0 0 0 0 0 0 0 0 0], len(s2) = 10, cap(s2) = 32
    t.Logf("s2 = %v, len(s2) = %d, cap(s2) = %d", s2, len(s2), cap(s2))
}

通过 make 方式可以设置初始化长度和容量,这是字面量创建切片所不具备的能力,并且这种方式创建的切片还支持批量拷贝功能!

func TestCopySlice(t *testing.T) {
    var s1 = []int{1, 3, 5, 7, 9}
    var s2 = make([]int, 10, 32)

    copy(s2, s1)

    // s2 = [1 3 5 7 9 0 0 0 0 0], len(s2) = 10, cap(s2) = 32
    t.Logf("s2 = %v, len(s2) = %d, cap(s2) = %d", s2, len(s2), cap(s2))

    var s3 []int

    copy(s3, s1)

    // s3 = [], len(s3) = 0, cap(s3) = 0
    t.Logf("s3 = %v, len(s3) = %d, cap(s3) = %d", s3, len(s3), cap(s3))
}

func copy(dst, src []Type) int 是切片之间拷贝的函数,神奇的是,只有目标切片是 make 方式创建的切片才能进行拷贝,不明所以,有了解的小伙伴还请指点一二!

切片的底层结构是动态数组,如果切片是基于数组截取而成,那么此时的切片从效果上来看,切片就是原数组的一个视图,对切片的任何操作都会反映到原数组上,这也是很好理解的.

那如果对切片再次切片呢,或者说切片会不会越界,其实都比较简单了,还是稍微演示一下,重点就是动态数组的底层结构.

func TestSliceOutOfBound(t *testing.T) {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

    s1 := arr[2:6]
    // s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 6
    t.Logf("s1 = %v, len(s1) = %d, cap(s1) = %d", s1, len(s1), cap(s1))

    s2 := s1[3:5]
    // s2 = [5 6], len(s2) = 2, cap(s2) = 3
    t.Logf("s2 = %v, len(s2) = %d, cap(s2) = %d", s2, len(s2), cap(s2))
}

[] 只能访问 len(arr) 范围内的元素,[:] 只能访问 cap(arr) 范围内的元素,一般而言 cap >= len 所以某些情况看起来越界,其实并不没有越界,只是二者的标准不同!

我们知道切片 slice 的内部数据结构是基于动态数组,存在三个重要的变量,分别是指针 ptr,个数 len 和容量 cap ,理解了这三个变量如何实现动态数组就不会掉进切片的坑了!

个数 len 是通过下标访问时的有效范围,超过 len 后会报越界错误,而容量 cap 是往后能看到的最大范围,动态数组的本质也是控制这两个变量实现有效数组的访问.

行く-コンテナに関するスライス-outOfBound-len.png

因为 s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 6 ,所以 [] 访问切片 s1 元素的范围是[0,4) ,因此最大可访问到s1[3],而 s1[4] 已经越界了!

行く-コンテナに関するスライス-outOfBound-cap.png

因为 s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 6 ,所以 [:] 根据切片 s1 创建新切片的范围是 [0,6] ,因此最大可访问范围是 s1[0:6] ,而 s1[3:7] 已经越界!

集合 map

集合是一种键值对组成的数据结构,其他的主流编程语言也有类似概念,相比之下,Go 语言的 map 能装载的数据类型更加多样化.

  • 字面量创建 map 换行需保留逗号 ,
func TestMap(t *testing.T) {
    m1 := map[string]string{
        "author": "snowdreams1006",
        "website": "snowdreams1006",
        "language": "golang",
    }

    // map[name:snowdreams1006 site:https://snowdreams1006.github.io]
    t.Log(m1)
}

一对键值对的结尾处加上逗号 , 可以理解,但是最后一个也要有逗号这就让我无法理解了,Why ?

  • make 创建的 map 和字面量创建的 map 默认初始化零值不同
func TestMapByMake(t *testing.T) {
    // empty map
    m1 := make(map[string]int)

    // map[] false
    t.Log(m1, m1 == nil)

    // nil
    var m2 map[string]int

    // map[] true
    t.Log(m2, m2 == nil)
}

make 函数创建的 map 是空 map,而通过字面量形式创建的 mapnil,同样的规律也适合于切片 slice.

  • range 遍历 map 是无序的
func TestMapTraverse(t *testing.T) {
    m := map[string]string{
        "name": "snowdreams1006",
        "site": "https://snowdreams1006.github.io",
    }

    // map[name:snowdreams1006 site:https://snowdreams1006.github.io]
    t.Log(m)

    for k, v := range m {
        t.Log(k, v)
    }

    t.Log()

    for k := range m {
        t.Log(k)
    }

    t.Log()

    for _, v := range m {
        t.Log(v)
    }
}

这里再一次遇到 range 形式的遍历,忽略键或值时用 _ 占位,也是和数组,切片的把遍历方式一样,唯一的差别就是 map 没有索引,遍历结果也是无序的!

  • 获取元素时需判断元素是否存在
func TestMapGetItem(t *testing.T) {
    m := map[string]string{
        "name": "snowdreams1006",
        "site": "https://snowdreams1006.github.io",
    }

    // snowdreams1006
    t.Log(m["name"])

    // zero value is empty string
    t.Log(m["author"])

    // https://snowdreams1006.github.io
    if site, ok := m["site"]; ok {
        t.Log(site)
    } else {
        t.Log("key does not exist ")
    }
}

Go 语言的 map 获取不存在的键时,返回的是值对应类型的零值,map[string]string 返回的默认零值就是空字符串,由于不会报错进行强提醒,这也就要求我们调用时多做一步检查.当键值对存在时,第二个返回值返回 true,不存在时返回 false.

  • 删除键值对时用 delete 函数
func TestMapDeleteItem(t *testing.T) {
    m := map[string]string{
        "name": "snowdreams1006",
        "site": "https://snowdreams1006.github.io",
    }

    // map[name:snowdreams1006 site:https://snowdreams1006.github.io]
    t.Log(m)

    delete(m, "name")

    // map[site:https://snowdreams1006.github.io]
    t.Log(m)

    delete(m, "id")

    // map[site:https://snowdreams1006.github.io]
    t.Log(m)
}

delete(map,key) 用于删除 map 的键值对,如果想要验证是否删除成功,别忘了使用 value,ok := m[k] 确定是否存在指定键值对

  • slice,map,func 外,其余类型均可键

因为 map 是基于哈希表实现,所以遍历是无序的,另一方面因为 slice,map,func 不可比较,因为也不能作为键.当然若自定义类型 struc 不包含上述类型,也可以作为键,并不要求实现 hashcodeequal 之类的.

  • value 可以承载函数 func 类型
func TestMapWithFunValue(t *testing.T) {
    m := map[int]func(op int) int{}

    m[1] = func(op int) int {
        return op
    }
    m[2] = func(op int) int {
        return op * op
    }
    m[3] = func(op int) int {
        return op * op * op
    }

    // 1 4 27
    t.Log(m[1](1), m[2](2), m[3](3))
}

再一次说明函数是一等公民,这部分会在以后的函数式编程中进行详细介绍.

没有 set

Go 的默认类型竟然没有 set 这种数据结构,这在主流的编程语言中算是特别的存在了!

正如 Go 的循环仅支持 for 循环一样,没有 while 循环一样可以玩出 while 循环的效果,靠的就是增强的 for 能力.

所以,即使没有 set 类型,基于现有的数据结构一样能实现 set 效果,当然直接用 map 就可以封装成 set.

func TestMapForSet(t *testing.T) {
    mySet := map[int]bool{}

    mySet[1] = true

    n := 3

    if mySet[n] {
        t.Log("update", mySet[n])
    } else {
        t.Log("add", mySet[n])
    }

    delete(mySet, 1)
}

使用 map[type]bool 封装实现 set 禁止重复性元素的特性,等到讲解到面向对象部分再好好封装,这里仅仅列出核心结构.

知识点总结梳理

Go 语言是十分简洁的,不论是基础语法还是这一节的内建容器都很好的体现了这一点.

数组作为各个编程语言的基础数据结构,Go 语言和其他主流的编程语言相比没有什么不同,都是一片连续的存储空间,不同之处是数组是值类型,所以也是可以进行比较的.

这并不是新鲜知识,毕竟上一节内容已经详细阐述过该内容,这一节的重点是数组的衍生版切片 slice .

因为数组本身是特定长度的连续空间,因为是不可变的,其他主流的编程语言中有相应的解决方案,其中就有不少数据结构的底层是基于数组实现的,Go 语言的 slice 也是如此,因此个人心底里更愿意称其为动态数组!

切片 slice 的设计思路非常简单,内部包括三个重要变量,包括数组指针 ptr,可访问元素长度 len 以及已分配容量 cap .

継続的にスライスに新しい要素を追加するときは、必ず割り当てられた最大容量に達している、それは自動的にそれ以外の場合は、減容化、動的に制御する機能を有効にし、拡大をスライスします!

  • 配列の要素数が指定され、スライスの不特定多数であります
func TestArrayAndSlice(t *testing.T) {
    // array
    var arr1 [3]int
    // slice
    var arr2 []int

    // [0 0 0] []
    t.Log(arr1,arr2)
}
  • ベースのスライス配列の作成は、元の配列の図です。
func TestArrayAndSliceByUpdate(t *testing.T) {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    // arr =  [0 1 2 3 4 5 6 7 8 9]
    t.Log("arr = ", arr)

    s := arr[2:6]

    // before update s = [2 3 4 5], arr = [0 1 2 3 4 5 6 7 8 9]
    t.Logf("before update s = %v, arr = %v", s, arr)

    s[0] = 666

    // after update s = [666 3 4 5], arr = [0 1 666 3 4 5 6 7 8 9]
    t.Logf("after update s = %v, arr = %v", s, arr)
}
  • 追加または削除要素は、スライスされた新しいスライスを返します
func TestArrayAndSliceIncreasing(t *testing.T) {
    var s []int

    fmt.Println("add new item to slice")

    for i := 0; i < 10; i++ {
        s = append(s, i)

        fmt.Printf("s = %v, len(s) = %d, cap(s) = %d\n", s, len(s), cap(s))
    }

    fmt.Println("remove item from slice")

    for i := 0; i < 10; i++ {
        s = s[1:]

        fmt.Printf("s = %v, len(s) = %d, cap(s) = %d\n", s, len(s), cap(s))
    }
}
  • [index]アクセススライスと要素のみのスライスlenに関連する[start:end]新しい、元のスライスを作成するには、単にスライスcapについて
func TestArrayAndSliceBound(t *testing.T) {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    s1 := arr[5:8]

    // s1[0] = 5, s1[2] = 7
    t.Logf("s1[0] = %d, s1[%d] = %d", s1[0], len(s1)-1, s1[len(s1)-1])
    // s1 = [5 6 7], len(s1) = 3, cap(s1) = 5
    t.Logf("s1 = %v, len(s1) = %d, cap(s1) = %d", s1, len(s1), cap(s1))

    s2 := s1[3:5]

    // s2[0] = 8, s2[1] = 9
    t.Logf("s2[0] = %d, s2[%d] = %d", s2[0], len(s2)-1, s2[len(s2)-1])
    // s2 = [8 9], len(s2) = 2, cap(s2) = 2
    t.Logf("s2 = %v, len(s2) = %d, cap(s2) = %d", s2, len(s2), cap(s2))
}
  • のみmapなしset
func TestMapAndSet(t *testing.T) {
    m := map[string]string{
        "name": "snowdreams1006",
        "site": "https://snowdreams1006.github.io",
        "lang": "go",
    }

    // https://snowdreams1006.github.io
    if site, ok := m["site"]; ok {
        t.Log(site)
    } else {
        t.Log("site does not exist ")
    }

    s := map[string]bool{
        "name": true,
        "site": true,
        "lang": true,
    }

    // Pay attention to snowdreams1006
    if _, ok := m["isFollower"]; ok {
        t.Log("Have an eye on snowdreams1006")
    } else {
        s["isFollower"] = true
        t.Log("Pay attention to snowdreams1006")
    }
}
  • delete機能は設定削除mapキーと値のペアを
func TestMapAndSetByDelete(t *testing.T) {
    m := map[string]string{
        "name": "snowdreams1006",
        "site": "https://snowdreams1006.github.io",
        "lang": "go",
    }

    delete(m, "lang")

    // delete lang successfully
    if _,ok := m["lang"];!ok{
        t.Log("delete lang successfully")
    }
}

Go言語が組み込まれていた容器ではありませんGetそれは?あなたが間違った表現する場所を持っている場合は、公共の番号[スノードリームインテクノロジ]学習交流、ほとんど進展毎日を取るために歓迎し、ヘクタール、私を修正してください!

スノードリームイン技術.PNG

おすすめ

転載: www.cnblogs.com/snowdreams1006/p/11374595.html