热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GO数组与切片详解

GO数组与切片一:数组数组是存放元素的容器,必须指定存放的元素的类型和容量(长度)数组是值类型,赋值和传参会复制整个数组。因此改变副

GO 数组与切片


一:数组

数组是存放元素的容器,必须指定存放的元素的类型和容量(长度)

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。

注:数组的长度是数组类型的一部分,即如果两个数组的长度不一样是无法进行比较的

(一):数组定义

var 数组变量名 [元素数量]类型

数组的长度必须是常量,并且长度是数组的一部分。一旦定义,长度不能变。[3]int 和 [4]int是不同的类型

数组可以通过下标进行访问,下标是从0开始的,最后一个元素下标是len-1。

var a [3]int

(二):数组初始化

如果不初始化,默认元素都是零值(布尔值:false,整型和浮点型都为0,字符串为 “”)

方式一:

指定长度

package mainimport "fmt"func main() {var a1 [3]bool// 初始化方式一a1 = [3]bool{true, true, true}fmt.Println(a1)
}

方式二:

根据初始值自动推断数组的长度是多少

package mainimport "fmt"func main() {// 初始化方式二a2 := [...]int{0,1,2,3,43,44}fmt.Println(a2)
}

方式三

根据索引初始化

package mainimport "fmt"func main() {// 初始化方式三a3 := [5]int{0:1,4:5}fmt.Println(a3)
}

(三):数组遍历

方式一

for循环遍历

package mainimport "fmt"func main() {// 方式一,for循环name := [...]string{"小红","小明","李华"}for i := 0; i }

方式二

for range

package mainimport "fmt"func main() {// 方式二,for rangefor k,v := range name {fmt.Println(k,v)}
}

(四):多维数组

多维数组创建

package mainimport "fmt"func main() {// 多维数组// [[1,2],[3,4],[5,6]]var a5 [3][2]inta5 = [3][2]int{[2]int{1,2},[2]int{3,4},[2]int{5,6},}fmt.Println(a5)
}

多维数组的遍历

package mainimport "fmt"func main() {// 多维数组遍历for _,v1 := range a5{fmt.Println(v1)for _, v2 := range v1{fmt.Println(v2)}}
}

二:切片

​ 因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。比如求和时只能对相同个数的数组求和,而且也无法继续向数组中添加新元素。

​ 切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。

​ 切片不保存数据,所以针对切片的修改都是针对底层数据的修改。

(一):切片的定义及初始化

package mainimport "fmt"func main() {// 切片定义var s1 []int // 定义一个int类型的切片var s2 []string // 定义一个string类型的切片fmt.Println(s1 == nil) // 判断s1是否为空var s3 = []bool{true,false,true} // 初始化fmt.Println(s1,s2,s3)fmt.Println(s3 == nil) // 判断s3是否为空
}

(二):切片长度和容量

切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量

切片指向一个底层的数组,是一个引用,并没有具体的值

切片的长度就是它元素的个数

切片的容量为底层数组从切片的第一个元素到最后的元素的数量

package mainimport "fmt"func main() {// 长度和容量s1 = []int{1,2,3}s2 = []string{"小红","小明","李华"}fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1),cap(s1))fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2),cap(s2))// 由数组得到切片的长度和容量s4 := [...]int{1,3,5,7,9,11,13}s5 := s4[0:4] // 对数组按索引切割,左包含右不包含s6 := s4[3:]// 容量为底层数组从切片的第一个元素到最后的元素的数量fmt.Printf("len(s5):%d cap(s5):%d\n", len(s5),cap(s5))fmt.Printf("len(s6):%d cap(s6):%d\n", len(s6),cap(s6))// 对切片进行切片s7 := s6[1:2]fmt.Printf("len(s7):%d cap(s7):%d\n", len(s7),cap(s7))
}// 结果
// len(s1):3 cap(s1):3
// len(s2):3 cap(s2):3
// len(s5):4 cap(s5):7
// len(s6):4 cap(s6):4
// len(s7):1 cap(s7):3

(三):make函数创建切片

make([]type, 数量,容量)

如果不传入容量的值则容量的值默认等于数量

package mainimport "fmt"func main() {// make函数创造切片s1 := make([]int, 5, 10)fmt.Printf("s1: %d, len(s1): %d, cap(s1): %d", s1, len(s1), cap(s1))
}

(四):切片详解

​ 切片就是一个框,框住了一块连续的内存。属于引用类型,真正的数据都是保存在底层数组中的。

​ 切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等的元素。切片唯一合法的比较操作时和nil比较。一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0.但是我们不能说一个长度和容量都是0的切片一定是nil。

var s1 []int // s1 == nil
// 切片为空,但s2 != nil,因为s2已开辟了一块内存空间
s2 := []int{}
s3 := make([]int,0) // s3 与 s2 同理

​ 所以要判断一个切片是否是空的,要用 len() 方法来确定,不能用 s==nil 来判定

(五):切片的赋值拷贝

package mainimport "fmt"func main() {// 切片赋值s2 := []int{1,2,3,4}s3 := s2 // s2,s3都指向了同一个底层数组fmt.Printf("s2为:%d, s3为: %d\n", s2, s3)s3[3] = 1000fmt.Printf("s2为:%d, s3为: %d\n", s2, s3)
}// 结果
// s2为:[1 2 3 4], s3为: [1 2 3 4]
// s2为:[1 2 3 1000], s3为: [1 2 3 1000]

s2,s3都指向了同一个底层数组[1 2 3 4],相当于在底层数组上框了一个框,当底层的数组发生改变时,s2,s3都会发生改变。

(六):切片的遍历

package mainimport "fmt"func main() {// 切片的遍历// for索引遍历for i := 0; i }

(七):切片追加元素

append()

​ 当使用索引添加元素时需注意索引越界问题,当切片的索引超过切片的容量时就会提示索引越界。可使用append()方法进行添加。

​ Go语言的内建函数append()可以为切片动态添加元素。每个切片会指向一个底层数组,这个数组能容纳一定数量的元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行扩容,此时切片指向的底层数组就会更换。扩容操作往往发生在append()函数调用时

注:必须用变量接受append的返回值

package mainimport "fmt"func main() {s1 := []string{"北京","上海","南京"}fmt.Printf("s1:%v, len(s1):%d, cap(s1):%d\n", s1, len(s1), cap(s1))// 调用append函数必须使用原来的切片变量接受返回值// append追加元素,原来的底层数组放不下的时候,Go底层就会把底层数组换一个s1 = append(s1, "山西") fmt.Printf("s1:%v, len(s1):%d, cap(s1):%d\n", s1, len(s1), cap(s1))// append 只能添加字符串,当传入变量时需将变量拆开s2 := []string{"成都","杭州","西安"}s1 = append(s1,s2...)
}

扩容策略:

  • 首先判断,如果新申请容量大于旧容量的2倍,最终容量就是新申请的容量
  • 否则判断,如果旧切片的长度小于1024,则最终容量就是旧容量的两倍
  • 否则判断,如果旧切片长度大于等于1024,则最终容量从旧容量开始循环增加原来的1/4,直到最终容量大于等于新申请的容量
  • 如果最终容量计算值溢出,则最终容量就是新申请容量

需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如int和string类型的处理方式就不一样

(八):copy复制

copy复制相当于将底层数组拷贝了出来放入另外一块内存空间

package mainimport "fmt"func main() {a1 := []int{1,3,5}a2 := a1a3 := make([]int,3,3)copy(a3,a1)fmt.Println(a1,a2,a3)a1[0] = 100fmt.Println(a1,a2,a3)
}// 结果
[1 3 5] [1 3 5] [1 3 5]
[100 3 5] [100 3 5] [1 3 5]

(九):从切片中删除元素

Go语言中没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。代码如下:

package mainimport "fmt"func main() {a1 := []int{1,3,5,7,9}// 将3和5删掉,...为解包的意思a1 = append(a1[:1], a1[3:]...)fmt.Println(a1)
}

(十):指针

Go语言中不存在指针操作,只需要记住两个符号:

  1. & 取地址
  2. * 根据地址取值

package mainimport "fmt"func main() {// 取地址s := 123p := &sfmt.Println(p)fmt.Printf("%T\n", p)// 根据内存地址取值m := *pfmt.Println(m)fmt.Printf("%T\n", m)
}// 结果
0xc0000a2058
*int
123
int

(十一):new

内存分配

make和new的区别:

  • make和new都是用来申请内存的
  • new很少用,一般用来给基本数据类型申请内存,string/int返回的是对应类型的指针
  • make是用来给slice、map、chan申请内存的,make函数返回的是对应的这三个类型本身

(十二):map

Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

形式:map[keyType]valueType

package mainimport "fmt"func main() {var m1 map[string]intfmt.Println(m1 == nil) // 还没有初始化(还没有开辟内存空间)// 初始化m1 = make(map[string]int, 10) // 要估算好该map的容量,避免在程序运行过程中动态扩容m1["xiaoming"] = 18m1["kid"] = 25fmt.Println(m1)fmt.Println(m1)
}# 结果
true
map[kid:25 xiaoming:18]
25

判断key是否存在

package mainimport "fmt"func main() {var m1 map[string]intm1 = make(map[string]int, 10) m1["xiaoming"] = 18m1["kid"] = 25// 判断map中是否有某个值// 方式一:直接打印,如果不存在则返回0值fmt.Println(m1["hack"])// 方式二:判断(推荐使用),使用value和ok两个变量来接受map的值,value为存在key时的值,ok是一个布尔值,如果存在则为true,如果不存在则为falsevalue,ok := m1["hack"]if !ok {fmt.Println("不存在这个值")} else {fmt.Println(value)}
}// 结果
0
不存在这个值

map的遍历

package mainimport "fmt"func main() {var m1 map[string]intm1 = make(map[string]int, 10) m1["xiaoming"] = 18m1["kid"] = 25// map的遍历for k,v := range m1 {fmt.Println(k,v)}
}// 结果
xiaoming 18
kid 25

删除键值对

delete(map,key)

map:表示要删除键值对的map

key:表示要删除的额键值对的键

package mainimport "fmt"func main() {var m1 map[string]intm1 = make(map[string]int, 10) m1["xiaoming"] = 18m1["kid"] = 25// 删除键值对delete(m1,"kid")fmt.Println(m1)delete(m1,"hack") // 如果删除的key不存在,则不进行任何操作
}# 结果
map[xiaoming:18]

元素为map类型的切片

注:对于map和切片都必须进行初始化

package mainimport "fmt"func main() {// 元素类型为map的切片var s1 = make([]map[int]string, 1, 10)// 必须对内部的map做初始化s1[0] = make(map[int]string, 1)s1[0][1] = "张三"fmt.Println(s1)
}

值为切片类型的map

注:对于map和切片都必须进行初始化

package mainimport "fmt"func main() {// 值为切片类型的mapm1 := make(map[string][]int, 5)m1["体重"] = []int{1,2,3,4}fmt.Println(m1)
}


推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • (三)多表代码生成的实现方法
    本文介绍了一种实现多表代码生成的方法,使用了java代码和org.jeecg框架中的相关类和接口。通过设置主表配置,可以生成父子表的数据模型。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
author-avatar
严气
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有