zoukankan      html  css  js  c++  java
  • GO学习笔记 数组与切片

    一.数组

    1.数组的介绍

    数组可以可以存放多个同一类型数据。数组也是一种数据类型,在GO,数组是值类型。

    举例:

    package main
    
    import "fmt"
    
    func main(){
        // 使用数组
        // 定义一个数组
        var nums [6]float64;
        // 给数组的每一个元素赋值
        nums[0] = 3.0
        nums[1] = 5.0
        nums[2] = 1.0
        nums[3] = 3.4
        nums[4] = 2.0
        nums[5] = 50.0
    
        // 遍历数字求平均值
        total := 0.0
        for i := 0; i < len(nums); i++ {
            total += nums[i]
        }
        avg_num := fmt.Sprintf("%.2f",total / float64(len(nums)))
        fmt.Println(avg_num)
    }
    

    2.数组定义和内存布局

    数组的定义

    var 数组 [数组大小]数据类型

    如:

    var a  [10]int;

    赋值a[0],a[1]....a[9]

    数组的地址可以通过数组名来获取  &arr

    数组的首地址就是数组第一个值的地址的。 &arr[0]

    数组第二个值的地址就是第一个地址加这个数组类型的字节数,这边如果是int就加8个字节。

    package main
    
    import "fmt"
    
    func main() {
        var arr [4]int;
        fmt.Println("第一个地址:",&arr[0])  
        fmt.Println("第二个地址:",&arr[1])
        fmt.Println("第三个地址:",&arr[2])
        fmt.Println("第四个地址:",&arr[3])
        // 打印结果:
        // 第一个地址: 0xc000052120
        // 第二个地址: 0xc000052128
        // 第三个地址: 0xc000052130
        // 第四个地址: 0xc000052138
    }
    

    3.数组的使用

    访问数组元素

    数组名[下标] 比如:你要使用a数组的第三个元素  a[2]

    package main
    
    import "fmt"
    
    func main() {
        var intArr [4]int
        intArr[0] = 10
        intArr[1] = 20
        intArr[2] = 30
        intArr[3] = 40
    
        fmt.Println("第一个地址:", &intArr[0])
        fmt.Println("第二个地址:", &intArr[1])
        fmt.Println("第三个地址:", &intArr[2])
        fmt.Println("第四个地址:", &intArr[3])
        // 打印结果:
        // 第一个地址: 0xc000052120
        // 第二个地址: 0xc000052128
        // 第三个地址: 0xc000052130
        // 第四个地址: 0xc000052138
    
        // 遍历数组打印
        for i := 0; i < len(intArr); i++ {
            fmt.Println(intArr[i])
        }
    }
    

    4.四种初始化数组的方式

    package main
    
    import "fmt"
    
    func main() {
        // 四种数组的初始化
        // 法一
        var numArr01 [3]int = [3]int{1, 2, 3}
        fmt.Println("numArr01", numArr01)
    
        // 法二
        var numArr02 = [3]int{4, 5, 6}
        fmt.Println("numArr02", numArr02)
    
        // 法三
        var numArr03 = [...]int{7, 8, 9}
        fmt.Println("numArr03", numArr03)
    
        // 法四
        var numArr04 = [...]int{1:11, 0:10, 2:12}  // 按下表的位置顺序输出,有序
        fmt.Println("numArr04", numArr04)
    }
    

    5.数组的变量

    方式1 :常规变量

    package main
    
    import "fmt"
    
    func main() {
        // for 遍历数组
        var arrDemo = [...]int{1,2,3,4,5}
        for i := 0; i < len(arrDemo); i++ {
            fmt.Println(arrDemo[i])
        }
    }
    

    方式二:for-range结构遍历

    GO 语言一种独有的结构,可以用来遍历访问数组的元素。

    基本语法

    for index,value := range arr {

    ....

    }

    说明:

    1)第一个返回值index是数组的下标

    2)第二个value是在该下标位置的值

    3)仅在for循环内部可见的局部变量

    4)遍历数组元素的时候,如果不想使用下标index,可以直接把下标index标为下划线_

    5indexvalue 的名称不是固定的,可自定义

    举例:

    package main
    
    import "fmt"
    
    func main() {
        var arrDemo = [...]int{1,2,3,4,5}
        // for-range遍历方式
        for index, value := range arrDemo{
            fmt.Println(index,value)
        }
    }
    

    6.数组的使用注意事项和细节

    1)数组是多个相同类型数据的结合,一个数组一旦声明定义了,其长度是固定的,不能动态变化。

    2var arr[]int 这时arr就是一个slice切片。

    3)数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用

    4)数组创建后,如果没有赋值,有默认值

    数值类型数组:默认值为0

    字符串数组: 默认为””

    bool数组:  默认值为false

    5)使用数组的步骤1.声明数组并开辟空间 2.给数组各个元素赋值 3. 使用数组

    6)数组的下标是从0开始的

    7)数组的下标必须在指定范围内使用,否则报panic:数组越界,比如:

    var arr [5]int 则有效下标为0-4

    8GO的数组属值类型,在默认情况下是值传递,因此会进行拷贝。数组间不会互相影响。

    9)如想在其中函数中,去修改原来的数组,可以使用引用传递(指针方式)。

    10)长度是数组的一部分,在传递函数参数时,需要考虑数组的长度。

    举例:(8),(9

    package main
    
    import "fmt"
    
    func test(arr [3]int) {
        arr[0] = 88
    }
    
    func demo(arr *[3]int) {
        (*arr)[1] = 99 
    }
    
    func main() {
        arr := [3]int{11,22,33}
        fmt.Println(arr)
        test(arr)
        // 数组的值不会因为调用的函数改变而改变
        // 拷贝的思想
        fmt.Println(arr)
        // demo 修改指针的方法可以实现修改数组里面的值
        // 传地址
        demo(&arr)
        fmt.Println(arr)
    }
    

    7.数组的应用案例

    1.创建一个byte类型的26个元素的数组,分别放置A-Z。使用for循环访问所有元素并打印

    出来。提示:字符数据运算 A + 1 > B

    package main
    
    import "fmt"
    
    func main(){
    
        // 创建一个byte类型的26个元素的数组,分别放置A-Z。使用for循环访问所有元素并打印
        // 出来。提示:字符数据运算 A + 1 > B
    
        // 思路:
        //1.声明一个数组 var myChars [26]bytes
        // 2.使用for循环,利用 字符可以进行运算的特点来赋值  'A' + 1 -> 'B'
    
        var myChars [26]byte
        for i := 0; i < 26; i++ {
            myChars[i] = 'A' + byte(i)   // 注意需要将 i = > byte
        }
    
        for i:=0; i < 26; i++ {
            fmt.Printf("%c ",myChars[i])
        }
    }
    

    2.请求出一个数组最大值,并得到对应的下标

    package main
    
    import "fmt"
    
    func  main()  {
        // 请求出一个数组最大值,并得到对应的下标
    
        // 思路 假定第一个元素就是最大值,下标是0
        // 然后从第二个元素开始循环比较,如果发现有更大,则交换
        var intArr[5] int = [...]int {1,-1,-99,90,11}
        maxVal := intArr[0]
        maxValindex := 0
    
        for i:=1; i<len(intArr); i++ {
            if maxVal < intArr[i] {
                maxVal = intArr[i]
                maxValindex = i
            }
            
        }
        fmt.Printf("maxVal=%v maxValindex=%v",maxVal,maxValindex)
    }
    

    3.请求一个数组的和和平均值。 for range

    package main
    
    import "fmt"
    
    func main(){
        // 请求一个数组的和和平均值。 for range
        var intArr[5] int = [...]int {1,-1,-5,90,11}
        sum := 0
        for _,val := range intArr{
            // 求和
            sum += val
        }
        fmt.Println(sum,float64(sum)/float64(len(intArr)))
    
    }
    

    4.一个数组的反转

    package main
    
    import "fmt"
    import "math/rand"
    import "time"
    
    func main()  {
        // 一个数组的反转
        // 要求:随机生成5个数,并将其反转打印
        // rand.Intn()  生成(0,n)的随机数
        // 反转打印,交换的次数是 len / 2
        // 第一个与倒数第一  第二与倒数第二
        var intArr [5] int 
        // 为了每次生成的随机数不一样
        rand.Seed(time.Now().UnixNano())
        for i:=0; i<len(intArr); i++ {
            intArr[i] = rand.Intn(100)  // 
        }
        fmt.Println(intArr)
        temp := 0
        for i:=0; i < len(intArr) / 2; i ++{
            temp = intArr[len(intArr) - 1 - i]
            intArr[len(intArr) -1 - i] = intArr[i]
            intArr[i] = temp
        }
        fmt.Println(intArr)
    }
    

    二.切片 Slice

    1.切片的基本介绍

    1slice 就是切片的意思

    2)切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。

    3)切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度len(slice) 都一样。

    4)切片的长度是可以变化的,因此切片是一个可以动态变化的数组

    5)切片定义的基本语法:(特别像python的列表)

    var 切片名  []  类型    

    举例:

    package main
    
    import "fmt"
    
    func main() {
        // 先定义一个数组
        var intArr [5]int = [...]int{1, 22, 33, 66, 99}
    
        // 定义一个切片
        slice := intArr[1:3]
        // 表示引用 intArr数组的下标为1的元素 到 下标为3 的元素,结束下标的元素不算在内
        fmt.Println(slice)       // [22 33]
        fmt.Println(len(slice))  // 2
        fmt.Println(cap(slice)) // 切片的容量是可以动态变化的,一般是slice的两倍 
    
    }
    

    2.切片在内存中形式

    1slice的确是一个引用类型

    2slice从底层来说,其实就是一个数据结构struct结构体

    3slice 里面有三个部分,第一部分是引用数据开始的指针地址,第二部分是存放数据大小,第三部分是存放cap容量。

     slice[1] = 44
        // 改变切片的值,会使得切片引用的数组相应的数据也改变
        fmt.Println(intArr[2]) /
    

    3.切片的使用

    1)第一种方式

    定义一个切片,然后让切片去引用一个已经创建好的数组。

    如下:

    package main
    
    import "fmt"
    
    func main() {
        // 先定义一个数组
        var intArr [5]int = [...]int{1, 22, 33, 66, 99}
    
        // 定义一个切片
        slice := intArr[1:3]
        // 表示引用 intArr数组的下标为1的元素 到 下标为3 的元素,结束下标的元素不算在内
        fmt.Println(slice)       // [22 33]
    }
    

    2)第二种方式

    通过make来创建切片

    基本语法:

    var 切片名 [] type = make([],len,[cap])

    type:就是数据类型

    len:大小

    cap:指定切片容量,可选

    package main
    
    import "fmt"
    
    func main() {
        var slice []int = make([]int, 4, 10)
        fmt.Println(slice)
        fmt.Println("slice len=",len(slice),"slice cap=",cap(slice))
        slice[0] = 100
        slice[2] = 200
        fmt.Println(slice)
    
    }
    

    总结:

    1—通过make方式创建切片可以指定切片的大小和容量

    2—如果没有给切片的各个元素赋值,那么就会使用默认值(int,float=>0 string=>””,bool=>false

    3—通过make方式创建的切片对应的数组是由make底层维护,对外不可见

    3)第三种方式

    定义一个切片,直接就指定具体数组,使用原理类似make的方式。

    package main
    
    import "fmt"
    
    func main() {
        // 第三种方式
        var strSlice []string = []string {"tom","jack","mary"}
    
        fmt.Println(strSlice)
        
    }
    

    这种方法没有指定cap的大小,所有与len的大小相同。

    4.切片的遍历

    切片的遍历和数组一样,也有两种方式。

    1for 循环常规方式遍历

    2for-range结构遍历切片

    为上面两种方式举例:

    package main
    
    import "fmt"
    
    func main() {
        // 使用常规的for循环遍历切片
        var arr [5]int = [...]int{10, 20, 30, 40, 50}
        slice := arr[1:4] // 第二个元素到第4个元素
        for i := 0; i < len(slice); i++ {
            fmt.Printf("i=%v v=%v
    ",i,slice[i])
        }
    
        // 使用for--range方式遍历切片
        for i, v := range slice {
            fmt.Printf("i=%v v=%v
    ", i, v)
        }
    }
    

    5.切片注意事项和细节说明

    1)切片初始化时 var slice = arr[startindex:endindex]

    说明:从arr数组下标为startindex开始,取到下标为endindex的元素(不含最后那一个元素)。

    2)切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是可以动态增长。

    1var slice = arr[0:end] 可以简写var slice = arr[:end]

    2var slice = arr[strat:len(arr)] 可以简写:var slice = arr[start:]

    3) var slice = arr[0:len(arr)] 可以简写:var slice = arr[:]

    3cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。

    4)切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。

    5)切片还可以切片。

    6)切片是引用类型,所以在传递时,遵守引用传递机制。要改元素会一起变化。

    举例:这个是一个原切片数据和修改后的数据都变化

    package main
    
    import "fmt"
    
    func main() {
        var slice1 []int 
        var arr [5]int = [...]int {1,2,3,4,5} // 数组
        slice1 = arr[:]   // slice1 属于arr数组的一部分,slice2的指针和slice1存的数据一样
        var slice2 = slice1
        slice2[0] = 10
        fmt.Println(slice1)
    
        fmt.Println(slice2)
    
        fmt.Println(arr)
        // [10 2 3 4 5]
        // [10 2 3 4 5]
        // [10 2 3 4 5]
    

    举例2:这个是一个原切片数据不变,修改后的数据变化

    package main
    
    import "fmt"
    
    func test(slice []int) {
        slice[0] = 100 // 这里修改slice[0]
    }
    
    func main(){
        //
        var slice = []int {1,2,3,4}
        fmt.Println("slice=",slice)  // [1,2,3,4]
        test(slice)
        fmt.Println("slice=",slice)
    }
    

    6.切片中的使用方法append

    append内置函数,可以对切片进行动态追加。

    切片append操作的底层原来分析:

    1)切片append操作的本质就是数组扩容

    2go底层会创建一个新的数组newArr(安装扩容大小)

    3)将slice原来包含的元素拷贝到新的数组newArr

    4slice重新引用到newArr(就是整个数组的指针地址变化)

    5)注意newArr是底层来维护的

    举例:

    package main
    
    import "fmt"
    
    func main(){
        // 用append 内置函数,可以对切片进行动态追加
        var slice []int = []int {100,200,300}
        fmt.Printf("slice=%v
    ",slice)
        slice = append(slice,400)
        fmt.Println("new_slice=",slice)
    
        var demo_slice []int  = []int{1,2,3}
        // 切片追加切片
        slice = append(slice,demo_slice...)  // ... 不能忘
        fmt.Println("new_new_slice=",slice)
        
    }
    

    7.切片的拷贝操作

    切片使用copy内置函数完成拷贝

    copy拷贝的数据,数据空间是独立的,之间相互不影响。

    如果要拷贝的切片元素个数大于新的切片的元素个数,那么只能够拷贝到新的切片的最大个数的数据到新的切片中。

    举例:

    package main
    
    import "fmt"
    
    func main(){
        var a []int = []int {1,2,3,4,5}
        var slice = make([]int, 10)  // 10个0的一个切片
        fmt.Println(slice)
    
        copy(slice,a)
        fmt.Println(slice)  // 将a上的元素复制到全为0的切片中
        // 切片才能进行拷贝操作
        // [0 0 0 0 0 0 0 0 0 0]
        // [1 2 3 4 5 0 0 0 0 0]
    
    }
    

    8.string slice

    1string底层是一个byte数组,因此string也可以进行切片处理

    2string是不可变的,也就是说不能通过str[0] = ‘z’ 方式来修改字符串的内容

    3)如果需要修改字符串,可以先将string-->[]byte再重新转回来 或者 []rune -->修改-->重写转成string (其实就相当于形成一个新的字符串)

    举例:修改字符串举例

    package main
    
    import "fmt"
    
    func main() {
        //string底层是一个byte数组 ,因此string也可以进行切片
        str := "hello_hello_hsz"
        // 使用切片获取hsz
        slice := str[12:]
        fmt.Println("slice=", slice)
    
        // string --> byte  转成byte数组
        arr := []byte(str)
        arr[0] = 'z'
        str = string(arr)
        fmt.Println("str=", str)
    
        // 可以处理英文和数字,但是不能处理中文
        // []byte 字节来处理,而一个汉字,是3个字节,因此就会出现乱码
        // 解决方法: string 转成 []rune 因此rune是按字符来计算处理的,兼容汉字
        arr1 := []rune(str)
        arr1[0] = '好'
        str = string(arr1)
        fmt.Println("str=", str)
    }
    

    9.斐波那契数列放到切片中

    package main
    
    import "fmt"
    
    func fbn(n int) []uint64 {
        // 声明一个切片,切片大小n
        fbnSlice := make([]uint64, n)
        // 第一个数和第二个数的斐波那契为1
        if n == 1 {
            fbnSlice[0] = 1
        } else if n == 2 {
            fbnSlice[0] = 1
            fbnSlice[1] = 1
        } else {
            fbnSlice[0] = 1
            fbnSlice[1] = 1
            // 进行for循环来存放斐波那契的数列
            for i := 2; i < n; i++ {
                fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
            }
        }
        return fbnSlice
    }
    
    func main() {
        // 编写一个函数 fbn(n int )
        // 1. 可以接收一个 n int
        // 2.能够将斐波那契的数列放到切片中
        // 提示: slice[0] = 1  slice[1] = 1  slice[2] = 2   slice[3] = 3  slice[4] = 5
        fnbSlice := fbn(4)
        fmt.Println("fbnSlice=", fnbSlice)
    }
  • 相关阅读:
    orcal中创建和删除表空间和用户
    tomcat常用的优化和配置
    tomcat中如何禁止和允许主机或地址访问
    velocity生成静态页面代码
    java下载文件
    java上传文件
    数据库行列转换
    JDBC连接数据库详解
    java中插入myslq的datetime类型的
    简单的邮件发送mail.jar
  • 原文地址:https://www.cnblogs.com/hszstudypy/p/13598877.html
Copyright © 2011-2022 走看看