Go数组
<p> </p><p> </p>
<div class="cnblogs_code">
<pre> 1<span style="color: rgba(0, 0, 0, 1)">. 数组:是同一种数据类型的固定长度的序列。
</span>2. 数组定义:var a int,比如:var a int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
</span>3. 长度是数组类型的一部分,因此,var a int和var aint是不同的类型。
</span>4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
<span style="color: rgba(0, 0, 255, 1)">for</span> i := 0; i < len(a); i++<span style="color: rgba(0, 0, 0, 1)"> {
}
</span><span style="color: rgba(0, 0, 255, 1)">for</span> index, v :=<span style="color: rgba(0, 0, 0, 1)"> range a {
}
</span>5<span style="color: rgba(0, 0, 0, 1)">. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
</span>6<span style="color: rgba(0, 0, 0, 1)">. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
</span>7.支持 <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">==</span><span style="color: rgba(128, 0, 0, 1)">"</span>、<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">!=</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> 操作符,因为内存总是被初始化过的。
</span>8.指针数组 *T,数组指针 *T。</pre>
</div>
<p> </p>
<h3> 数组介绍</h3>
<p>数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。数组的长度是数组类型的组成部分。<br>因为数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类型,因此在Go语言中很少直接使用数组(不同长度的数组因为类型不同无法直接赋值)。<br>和数组对应的类型是切片,切片是可以动态增长和收缩的序列,切片的功能也更加灵活,但是要理解切片的工作原理还是要先理解数组。</p>
<p>数组的内存结构:</p>
<h3><img src="https://img2018.cnblogs.com/blog/1477786/201906/1477786-20190623004122122-1142837854.jpg" alt=""></h3>
<p> </p>
<h3><br>一维数组初始化</h3>
<p>var a int // 定义长度为3的int型数组, 元素全部为0<br>var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3<br>var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3<br>var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6</p>
<p>第一种方式是定义一个数组变量的最基本的方式,数组的长度明确指定,数组中的每个元素都以零值初始化。</p>
<p>第二种方式定义数组,可以在定义的时候顺序指定全部元素的初始化值,数组的长度根据初始化元素的数目自动计算。</p>
<p>第三种方式是以索引的方式来初始化数组的元素,因此元素的初始化值出现顺序比较随意。这种初始化方式和mapType类型的初始化语法类似。数组的长度以出现的最大的索引为准,没有明确初始化的元素依然用0值初始化。</p>
<p>第四种方式是混合了第二种和第三种的初始化方式,前面两个元素采用顺序初始化,第三第四个元素零值初始化,第五个元素通过索引初始化,最后一个元素跟在前面的第五个元素之后采用顺序初始化。</p>
<p> </p>
<h3>二维数组初始化</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">全局
var arr0 [</span>5]int
var arr1 [</span>2]int = [...]int{{1, 2, 3}, {7, 8, 9<span style="color: rgba(0, 0, 0, 1)">}}
局部:
a :</span>= int{{1, 2, 3}, {4, 5, 6<span style="color: rgba(0, 0, 0, 1)">}}
b :</span>= [...]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">...</span><span style="color: rgba(128, 0, 0, 1)">"</span>。</pre>
</div>
<p>代码:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import (
"fmt"
)
var arr0 int
var arr1 int = [...]int{{1, 2, 3}, {7, 8, 9}}
func main() {
a := int{{1, 2, 3}, {4, 5, 6}}
b := [...]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。
fmt.Println(arr0, arr1)
fmt.Println(a, b)
} </pre>
</div>
<p>输出结果:</p>
<div class="cnblogs_code">
<pre>[ ] [ ]
[[</span>1 2 3] ] [ ]</pre>
</div>
<p><strong>内置函数 len 和 cap 都返回数组长度 (元素数量)</strong></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
func main() {
a := int{}
println(len(a), cap(a))
}</pre>
</div>
<p>输出结果:</p>
<div class="cnblogs_code">
<pre>2 2</pre>
</div>
<p> </p>
<p> </p>
<h3>数组布局</h3>
<p>Go语言中数组是值语义(所谓值语义是一个对象被系统标准的复制方式复制后,与被复制的对象之间毫无关系,可以彼此独立改变互不影响)。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。如果数组较大的话,数组的赋值也会有较大的开销。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">var a = [...]int{1, 2, 3} // a 是一个数组
var b = &a // b 是指向数组的指针
fmt.Println(a, a) // 打印数组的前2个元素
fmt.Println(b, b) // 通过数组指针访问数组元素的方式和数组类似
for i, v := range b { // 通过数组指针迭代数组的元素
fmt.Println(i, v)
}</pre>
</div>
<p>其中b是指向a数组的指针,但是通过b访问数组中元素的写法和a类似的。还可以通过for range来迭代数组指针指向的数组元素。其实数组指针类型除了类型和数组不同之外,通过数组指针操作数组的方式和通过数组本身的操作类似,而且数组指针赋值时只会拷贝一个指针。但是数组指针类型依然不够灵活,因为数组的长度是数组类型的组成部分,指向不同长度数组的数组指针类型也是完全不同的。</p>
<p>可以将数组看作一个特殊的结构体,结构的字段名对应数组的索引,同时结构体成员的数目是固定的。内置函数len可以用于计算数组的长度,cap函数可以用于计算数组的容量。不过对于数组类型来说,len和cap函数返回的结果始终是一样的,都是对应数组类型的长度。</p>
<p> </p>
<h3>遍历数组</h3>
<p>我们可以用for循环来迭代数组。下面常见的几种方式都可以用来遍历数组:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">for i := range a {
fmt.Printf("a[%d]: %d\n", i, a)
}
for i, v := range b {
fmt.Printf("b[%d]: %d\n", i, v)
}
for i := 0; i < len(c); i++ {
fmt.Printf("c[%d]: %d\n", i, c)
} </pre>
</div>
<p>用for range方式迭代的性能可能会更好一些,因为这种迭代可以保证不会出现数组越界的情形,每轮迭代对数组元素的访问时可以省去对下标越界的判断。<br>用for range方式迭代,还可以忽略迭代时的下标:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">var times int
for range times {
fmt.Println("hello")
}
</pre>
</div>
<p> </p>
<h3>多维数组遍历</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main
import (
"fmt"
)
func main() {
var f int = [...]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
for k2, v2 := range v1 {
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
}
fmt.Println()
} <br>}</pre>
</div>
<p>输出结果:</p>
<div class="cnblogs_code">
<pre>(0,0)=1 (0,1)=2 (0,2)=3<span style="color: rgba(0, 0, 0, 1)">
(</span>1,0)=7 (1,1)=8 (1,2)=9</pre>
</div>
<p> </p>
<h3>不同数据类型的数组</h3>
<p>数组不仅仅可以用于数值类型,还可以定义字符串数组、结构体数组、函数数组、接口数组、管道数组等等:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">// 字符串数组
var s1 = string{"hello", "world"}
var s2 = [...]string{"你好", "世界"}
var s3 = [...]string{1: "世界", 0: "你好", }
// 结构体数组
var line1 image.Point
var line2 = [...]image.Point{image.Point{X: 0, Y: 0}, image.Point{X: 1, Y: 1}}
var line3 = [...]image.Point{{0, 0}, {1, 1}}
// 图像解码器数组
var decoder1 func(io.Reader) (image.Image, error)
var decoder2 = [...]func(io.Reader) (image.Image, error){
png.Decode,
jpeg.Decode,
}
// 接口数组
var unknown1 interface{}
var unknown2 = [...]interface{}{123, "你好"}
// 管道数组
var chanList = chan int{}</pre>
</div>
<p>我们还可以定义一个空的数组:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">var d int // 定义一个长度为0的数组
var e = int{} // 定义一个长度为0的数组
var f = [...]int{} // 定义一个长度为0的数组</pre>
</div>
<p>长度为0的数组在内存中并不占用空间。空数组虽然很少直接使用,但是可以用于强调某种特有类型的操作时避免分配额外的内存空间,比如用于管道的同步操作:</p>
<div class="cnblogs_code">
<pre> c1 :=<span style="color: rgba(0, 0, 0, 1)"> make(chan int)
go func() {
fmt.Println(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">c1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
c1 </span><-<span style="color: rgba(0, 0, 0, 1)"> int{}
}()
</span><-c1</pre>
</div>
<p>在这里,我们并不关心管道中传输数据的真实类型,其中管道接收和发送操作只是用于消息的同步。对于这种场景,我们用空数组来作为管道类型可以减少管道元素赋值时的开销。当然一般更倾向于用无类型的匿名结构体代替:</p>
<div class="cnblogs_code">
<pre> c2 :=<span style="color: rgba(0, 0, 0, 1)"> make(chan struct{})
go func() {
fmt.Println(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">c2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
c2 </span><- struct{}{} //<span style="color: rgba(0, 0, 0, 1)"> struct{}部分是类型, {}表示对应的结构体值
}()
</span><-c2</pre>
</div>
<p>我们可以用<code>fmt.Printf</code>函数提供的<code>%T</code>或<code>%#v</code>谓词语法来打印数组的类型和详细信息:</p>
<div class="cnblogs_code">
<pre> fmt.Printf(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">b: %T\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>, b)// b: int
fmt.Printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">b: %#v\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>, b) // b: int{1, 2, 3}</pre>
</div>
<p>在Go语言中,数组类型是切片和字符串等结构的基础。以上数组的很多操作都可以直接用于字符串或切片中。</p>
<p> </p>
<p> </p>
<p> </p>
<p>https://chai2010.cn/advanced-go-programming-book/ch1-basic/ch1-03-array-string-and-slice.html</p>
<p>https://www.kancloud.cn/liupengjie/go/574250</p><br><br>
来源:https://www.cnblogs.com/-wenli/p/11071198.html
頁:
[1]