第三章 内建容器 数组, 切片, map

前端之家收集整理的这篇文章主要介绍了第三章 内建容器 数组, 切片, map前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本章讲解了3方面的内容

1. 数组

2. 切片

3. map


 

一、数组

1. 数组的定义方式

var arr1 [5]int
arr2 := [3]int{1,3,1)">5}
arr3 := [...]2,1)">4,1)">6,1)">8,1)">10}

var grid [4][

fmt.Println(arr1,arr2,arr3)
fmt.Println(grid)

输出结果:

[0 0] [1 3 5] [2 4 6 8 ]
[[0]]

 

2. 数组遍历的方式: 

for i := 0; i < len(arr2); i++  {
    fmt.Println(arr2[i])
}
for i,v := range arr3 {
  fmt.Println(i,v)
}        

rang方式遍历的三种

// 第一种: 只获取数组的下标
for i := range arr3 {
    fmt.Println(arr3[i])
}

 第二种: 获取数组的下标和值
 range arr3 {
    fmt.Println(i,v)
}

 第三种: 只获取
for _,1)"> range arr3 {
    fmt.Println(v)
}

 

3. arr[5] 和 arr[3]是不同的类型

func printArray(arr [) {
    arr[0] = 100
     range arr {
        fmt.Println(v)
    }
}

func main() {
    var arr1 [
    arr2 := [}
    arr3 := [...]}

    var grid [

    fmt.Println(arr1,arr3)
    fmt.Println(grid)

    printArray(arr1)
    printArray(arr3)
     下面这个打印会报错,因为arr2是3个容量的数组
    // printArray(arr2)
}    

这里传递arr1 和arr3过去的时候,可以正常打印数组,但是传递arr2过去的时候,会报异常. 

cannot use arr2 (type [int) as type [int in argument to printArray

原因是: [3]int 和[5]int是不同的类型

 

4. 数组的传递是值拷贝类型. 

func printArray(arr [ range arr {
        fmt.Println(v)
    }
}

func main() {
    定义数组的三种方法
    var arr1 [}
     证明数组是值拷贝类型
    fmt.Println("证明数组是值拷贝类型")
    printArray(arr1)
    printArray(arr3)

    fmt.Println(arr1,arr3)
}

我们在函数printArray中修改数组第一个元素的值是100. 然后打印. 在打印原数组. 结果如下:

证明数组是值拷贝类型
100
0
4
6
8

[10]

 

5. 如何实现数组的地址传递呢? 使用指针

func printArray1(arr *[) {
    arr[0] = 100
    如何让数组实现值拷贝呢)
    printArray(&arr1)
    fmt.Println(arr1)
}

结果

如何让数组实现地址拷贝呢?
0100 0]

注意: 

  a. 在方法printArray中参数接收是一个地址类型. 并且传递参数的时候也传递一个地址类型

  b. 在arr[0] = 100赋值的时候,无需获取地址的值. 直接给指针类型的数组赋值即可

 

二、切片slice

1. 什么是slice

func main() {
     定义一个数组
    arr1 := [...]0,1)">5,1)">7}
    fmt.Println(arr1[2:6] = ",arr1[2:6])
    fmt.Println(arr1[2:] = 2:])
    fmt.Println(arr1[:6] = arr1[:] = ,arr1[:] )
}

结果

arr1[6] =  []
arr1[2:] =  [5 ]
arr1[:]
arr1[:] =  [7]

 

  a. 通过arr[a:b]方式获取的值就是slice

  b. slice是数组的一个视图. slice不是值传递的. slice内存储的是数组的地址

 2. 验证slice不是值传递

func updateSlice(s []) {
    s[0] =100
}

func main() {
    }

    s1 := arr1[:]
    s2 := arr1[:]
    
    fmt.Println(after updateSlice(s1))
    updateSlice(s1)
    fmt.Println(s1)
    updateSlice(s2)
    fmt.Println(s2)
    fmt.Println(arr1)
}

结果:

after updateSlice(s1)
s1 =  []
s2 =  []
arr1 =  [7]

可以看到很有趣的现象. s1 的第一个元素被改100. 同时影响了s2的第3个元素. s2的第一个元素修改后,s1,s2同时都影响了数组arr1.

由此可见: s1,s2 都是指向的数组的地址

将s传递给函数printSlice,在打印原来的s. 发现s的值变化了. 说明,切片传递是地址传递,而不是值传递. 

可是上一章讲指针的时候,不是说go中只有值拷贝一种类型么? 那么为什么slice不是值拷贝呢? 因为slice是数组的一个视图. (可以理解为,他取的是数组中指定元素的地址. 所以,slice不是值拷贝,他的元素是地址)

 

 思考: 在上面将数组的第5个问题,数组如何作为一个地址拷贝的方式作为参数传递到方法里面呢? 我们的做法是: 将数组作为一个指针传过去. 学习了slice,我们可以换一种方法.

 

func printArray1(arr []int) {
    arr[ range arr {
        fmt.Println(v)
    }
}

func main() {

    定义数组的三种方法
    var arr1 [}


    fmt.Println(arr1,arr3)

     如何让数组实现地址拷贝呢?
    fmt.Println(如何让数组实现地址拷贝呢?方法2)
    printArray1(arr3[:])
    fmt.Println(arr3)
}

结果:

[]
如何让数组实现地址拷贝呢?方法2
10]
1. printArray1(arr []int)接收参数的时候,使用arr []int就是切片. 如果是arr [5]int就是数组.
2.如何将一个数组传给接收切片的方法呢? 如下:
printArray1(arr3[:])

是不是很巧妙. 这种方法就不用使用& *了

 

3. reslice

我们对数组可以进行slice操作,对slice还可以继续进行slice操作,就是reslice

package main

import fmt

func updateSlice(s []}

    s2 := arr1[:]

    fmt.Println(reslice操作)
    fmt.Println(slice 后: ]
    fmt.Println(reslice 后: :]
    fmt.Println(再次reslice 后: updateSlice(s2)
    fmt.Println(再次reslice 然后修改第一个元素的值 后: 原数组:输出结果:

reslice操作
slice 后:  []
reslice 后:  [4]
再次reslice 后:  []
再次reslice 然后修改第一个元素的值 后:  []
原数组: [7]

我们看到对slice再次进行slice,就和对数组slice是类似的. 最终指向的都是数组中的地址

updateSlice(s2): 这个操作就比较有意思了. 我们看到,对二次reslice后的数组,修改他的第一个元素的值. 然后在打印原数组,发现,原数组的值也修改了. 再次证明,slice是数组的一个视图,最终存储的是数组的地址. 一旦被修改,会影响原数组.

 

4. slice扩展

看下面这个例子: 

]
    s2 := s1[3:]

    fmt.Println(s1 = 

结果:

s1 =  [5],s2 =  [6]

很奇怪: s1取的数组的4个元素. s2想要取s1的3-5个元素,可s1只有4个元素呀,s2还成功取出来了. 这是怎么回事呢?

为什么会这样呢?我们来分析一下

 

 

 最开始,数组中的值是0,1,2,3,4,5,6,7

当我们取s1切片的时候,s1是底层数组的一个视图,他取值是从2-6,虽然,他只取了4个元素,但是因为他是对底层的一个视图,所以他是可以看到底层后面的元素的.

 

 

然后取s2切片. 我们发现他去的是s1下标的3-5个元素.  虽然s1只有4个元素,因为他是可以看到底层其他后面的元素的,所以,s2能够把数组中第5个元素取出来.

 

 

 那么取出来对应的元素值时多少呢?对应到底层数组,他的值就是5和6

 

 

 

那么s1只有4个元素,为什么能够看到第五和第六个元素呢?

原来slice的底层结构是这么定义的

 

 

 slice定义了3个对象.

ptr是一个指针,记录的是切片的第一个地址(下面的方格代表数组), 

len是切片中元素的个数,如果获取超过元素个数的值,会报下标越界

cap: 是capacity,容量. 他存储的是从ptr指向的数组的元素开始,一直到数组结束的元素.

所以,s1[3:5]获取的是cap中的数据. 正常返回.

总结: 1. slice 可以向后扩展,但不能向前扩展. slice中cap的起始元素是从ptr开始

    2. s[i]不可以超越len(s),向后扩展可以超越len(s),但不可以超过底层数组cap(t)

 

5. 向slice添加元素

 定义一个数组--为什么这样定义就能够被识别为一个数组呢
    arr1 := [...]6]   2,5
    s2 := s1[5]      4,5
    fmt.Println(s2 = arr1 = 10)    s3 = = append(s3,1)">11)    s4 = = append(s4,1)">12)    s5 = = s5[s6 = 

结果

s2 =  [6],arr1 =  []
s3 =  [10],1)">]
s4 =  [10 11],1)">]
s5 =  [11 12],1)">10]

 

添加元素是append. 那么我们来分析下这段代码:

s2的值是5,6 原数组:[0 1 2 3 4 5 6 7]

s3在s2基础上增加了元素10,我们发现原数组变为:[0 1 2 3 4 5 6 10]. 这说明,append也是对slice的cap进行处理的

s4在s3基础上增加了元素11, 我们发现原数组较上一个没变化:[0 1 2 3 4 5 6 10]. 原因是什么呢?s4 增加的数组已经超过了cap范围,这时会重新开辟一块新的空间. 因为这块空间已经保存不下这个数据了.

s5的变化同s4,虽增加了一个元素,但是,原数组无变化

s5第二次打印可以证明,数组已经重新开辟了一块新的空间. 因为我对s5的第一个元素值进行了修改,但修改后数组的值没有变化,表明此时s5的切片指向的地址已经不是原来数组的地址了.

 

下面来看看这几个元素的len 和cap是如何变化的

= append(s2,s3,len(s3)= append(s3,s4,len(s4)= append(s4,s5,len(s5)
s2 =  [7],len(s2) 3
s3 =  [
s4 =  [
s5 =  [6    

我们打印出了每一次变化后len的值和cap的值. 从s4开始,增加了一个元素,原来的地址已经容纳不了这么多数据了,于是新开辟了一块空间. 元素的len是4,cap是当前数组的2倍. 已经不是原来的数组了

 

总结:

1. 添加元素时,如果超越capacity,那么会重新开辟一块更大的底层数组,把slice切片值copy过去. 原来的数组,如果有人用就依然存在,如果没人用就会被垃圾回收掉.

2. 由于是值传递,所以append必须要有一个返回值接收. 原因是: 当append的容量超过原来数组的时候,会新开辟一块空间,新开辟的空间需要有新的参数来接收.

 

7.slice的copy


}

func printSlice(s []) {
    fmt.Printf(len=%d,cap=%d \nvar sli [] 这样定义就是一个zero value,他的值时nil

     给sli赋值. 因为是zero value,可以直接复制
    0; i < 200 ;i ++  {
        printSlice(sli)
        sli = append(sli,1)">2*i+1)
    }
}

结果

len= 
len=87,1)">9,1)">1610,1)">11,1)">12,1)">13,1)">14,1)">15,1)">16,1)">17,1)">3218,1)">19,1)">20,1)">21,1)">22,1)">23,1)">24,1)">25,1)">26,1)">27,1)">28,1)">29,1)">30,1)">31,1)">32,1)">33,1)">6434,1)">35,1)">36,1)">37,1)">38,1)">39,1)">40,1)">41,1)">42,1)">43,1)">44,1)">45,1)">46,1)">47,1)">48,1)">49,1)">50,1)">51,1)">52,1)">53,1)">54,1)">55,1)">56,1)">57,1)">58,1)">59,1)">60,1)">61,1)">62,1)">63,1)">64,1)">65,1)">12866,1)">67,1)">68,1)">69,1)">70,1)">71,1)">72,1)">73,1)">74,1)">75,1)">76,1)">77,1)">78,1)">79,1)">80,1)">81,1)">82,1)">83,1)">84,1)">85,1)">86,1)">87,1)">88,1)">89,1)">90,1)">91,1)">92,1)">93,1)">94,1)">95,1)">96,1)">97,1)">98,1)">99,1)">100,1)">101,1)">102,1)">103,1)">104,1)">105,1)">106,1)">107,1)">108,1)">109,1)">110,1)">111,1)">112,1)">113,1)">114,1)">115,1)">116,1)">117,1)">118,1)">119,1)">120,1)">121,1)">122,1)">123,1)">124,1)">125,1)">126,1)">127,1)">128,1)">129,1)">256130,1)">131,1)">132,1)">133,1)">134,1)">135,1)">136,1)">137,1)">138,1)">139,1)">140,1)">141,1)">142,1)">143,1)">144,1)">145,1)">146,1)">147,1)">148,1)">149,1)">150,1)">151,1)">152,1)">153,1)">154,1)">155,1)">156,1)">157,1)">158,1)">159,1)">160,1)">161,1)">162,1)">163,1)">164,1)">165,1)">166,1)">167,1)">168,1)">169,1)">170,1)">171,1)">172,1)">173,1)">174,1)">175,1)">176,1)">177,1)">178,1)">179,1)">180,1)">181,1)">182,1)">183,1)">184,1)">185,1)">186,1)">187,1)">188,1)">189,1)">190,1)">191,1)">192,1)">193,1)">194,1)">195,1)">196,1)">197,1)">198,1)">199,1)"> 

Process finished with exit code 0
View Code

从打印结果可以看出. 1) 没有个切片赋初始值,他的默认长度是0,因此给切片append不会报错. 2) 每次cap容量不够的时候,增长是按照当前元素值的2倍增长的.

%v,len=%d,s,cap(s))
}

func main() {

    s1 := []}
    s2 := make([]int,1)">)
    s3 := make([])

    printSlice(s1)
    printSlice(s2)
    printSlice(s3)

    copy(s2,s1)
    printSlice(s2)
  
  copy(s3,s1)
  printSlice(s3)
}

结果:

[4],len= 
[0],1)">2],1)">2 

1) 定义切片可以使用的方法

  • var s1 []int. 初始值是nil,len是0 cap是0,可直接append
  • var s2 = []int{1,4},有4个元素,len是4,cap是4
  • var s3 = make([]int,16),有16个元素,初始值都是0,len是16,cap是16. 

 

2) copy是将s1 copy给s2.

3) 如果,目标容量小于原容量,则只copy目标容量个数的值

 

8. slice的delete

 slice不能直接delete,也就是没有提供delete方法,那么如果想要删除中间的某一个元素,怎么办呢?

删除元素值4)
    s4 := append(s2[:3],s2[:]...)
    printSlice(s4)

}

结果

[ 
删除s2的元素值4
[16 

s2的值是[1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0],len=16,cap=16

删除中间的元素4,怎么操作呢? 使用s4 := append(s2[:3],s2[4:]...)

注意:的一点是append的第二个参数是一个数组,而不是切片,那么如何将切片转换为数组呢? 在切片后面加三个点...

 

三、map

 map定义的三种方式

    // 第一种
    m := map[string]string {
        aaa":111bbb222ccc333ddd444 第二种
    m1 := make(map[int)   使用make,创建了一个empty map

     第三种
    var m2 map[int          使用var,创建了一个nil. nil和java中的null不同的是:nil可以参与运算.对null对象进行运算报异常
    fmt.Println(m,m1,m2)

重点看一下第二种和第三种方式有何不同.

方式二: 创建了一个empty map

方式三: 创建了一个nil 的map

虽然如此,二者都可以参与运算. go中的nil和java中的null不同的是: nil可以参与运算. 对null对象进行运算报异常

 

二.循环遍历使用range

循环遍历
    for k,1)"> range m {
        fmt.Println(k,v)  map是无需的,每次输出可能不一样
    }

 

三.判断key是否存在

 判断key是否存在
    if v,ok := m["aaa"]; ok {
        fmt.Println(v,ok)
    } else {
        fmt.Println(ok)
    }
v,ok := m["aaa"]用来判断key是否存在

四. 删除一个元素

delete(m,1)">")

 

猜你在找的Go相关文章