当前位置: 首页 > news >正文

免费创建网站 优帮云网页制作教程视频下载

免费创建网站 优帮云,网页制作教程视频下载,机械加工网站大全,网店推广分为哪几种类型1.概念 在Go语言中,切片(Slice)是一种基于数组的更高级的数据结构,它提供了一种灵活、动态的方式来处理序列数据。切片在Go中非常常用,因为它们可以动态地增长和缩小,这使得它们比固定大小的数组更加灵活。…

1.概念

在Go语言中,切片(Slice)是一种基于数组的更高级的数据结构,它提供了一种灵活、动态的方式来处理序列数据。切片在Go中非常常用,因为它们可以动态地增长和缩小,这使得它们比固定大小的数组更加灵活。

2.切片的内部实现

 切片是对数组的抽象表示,它包含三个要素:

1.  指向底层数组的指针
2.  切片的长度(元素数量)
3.  切片的容量(从开始到底层数组末尾的元素数量)

切片是一个有三个字段的数据结构,这些数据结构包含 Golang 需要操作底层数组的元数据: 

 这 3 个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)。

3.切片创建和初始化

在 Golang 中可以通过多种方式创建和初始化切片。是否提前知道切片所需的容量通常会决定如何创建切片

通过 make() 函数创建切片
使用 Golang 内置的 make() 函数创建切片,此时需要传入一个参数来指定切片的长度:

slice := make([]int, length, capacity)

这里,length是切片的长度,capacity是切片的容量。如果未指定capacity,它将等于length。

// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := make([]int, 5)

分别指定长度和容量时,创建的切片,底层数组的长度是指定的容量,但是初始化后并不能
访问所有的数组元素。

注意,Golang 不允许创建容量小于长度的切片,当创建的切片容量小于长度时会在编译时刻报错:

// 创建一个整型切片
// 使其长度大于容量
myNum := make([]int, 5, 3)

分别指定长度和容量时,创建的切片,底层数组的长度是指定的容量,但是初始化后并不能
访问所有的数组元素。

注意,Golang 不允许创建容量小于长度的切片,当创建的切片容量小于长度时会在编译时刻报错 

4.通过字面量创建切片

另一种常用的创建切片的方法是使用切片字面量,这种方法和创建数组类似,只是不需要指定[]运算符里的值。初始的长度和容量会基于初始化时提供的元素的个数确定:

// 创建字符串切片
// 其长度和容量都是 3 个元素
myStr := []string{"Jack", "Mark", "Nick"}
// 创建一个整型切片
// 其长度和容量都是 4 个元素
myNum := []int{10, 20, 30, 40}

当使用切片字面量创建切片时,还可以设置初始长度和容量。要做的就是在初始化时给出所需的长度和容量作为索引。下面的语法展示了如何使用索引方式创建长度和容量都是100个元素的切片:

// 创建字符串切片
// 使用空字符串初始化第 100 个元素
myStr := []string{99: ""}

区分数组的声明和切片的声明方式
当使用字面量来声明切片时,其语法与使用字面量声明数组非常相似。二者的区别是:如果在 [] 运算符里指定了一个值,那么创建的就是数组而不是切片。只有在 [] 中不指定值的时候,创建的才是切片。看下面的例子:

// 创建有 3 个元素的整型数组
myArray := [3]int{10, 20, 30}
// 创建长度和容量都是 3 的整型切片
mySlice := []int{10, 20, 30}

切片的复制:

当你有一个切片或者数组的时候,当希望将其元素作为单独的参数传递给一个函数时候,可以使用...操作符

func sum(x, y, z int) int {return x + y + z
}numbers := []int{1, 2, 3}
result := sum(numbers...) // 将 numbers 切片中的元素作为参数传递给 sum 函数
fmt.Println(result) // 输出: 6

在这个例子中,numbers...将切片numbers中的元素1,2,3展开为sum函数的参数 

5.nil 和空切片

有时,程序可能需要声明一个值为 nil 的切片(也称nil切片)。只要在声明时不做任何初始化,就会创建一个 nil 切片

// 创建 nil 整型切片
var myNum []int

在 Golang 中,nil 切片是很常见的创建切片的方法。nil 切片可以用于很多标准库和内置函数。在需要描述一个不存在的切片时,nil 切片会很好用。比如,函数要求返回一个切片但是发生异常的时候。下图描述了 nil 切片的状态:

空切片和 nil 切片稍有不同,下面的代码分别通过 make() 函数和字面量的方式创建空切片: 

// 使用 make 创建空的整型切片
myNum := make([]int, 0)
// 使用切片字面量创建空的整型切片
myNum := []int{}

区别:
1.  内存分配:
•  nil 切片:没有指向任何底层数组,没有任何内存分配。
•  空切片:指向一个长度为0的底层数组,但这个数组可能已经分配了内存(尽管长度为0)。


2.  函数返回值:
•  使用 nil 可以表示“没有值”,这在错误处理和可选值中很有用。
•  空切片通常用于表示一个有效的切片,但当前没有元素。

 

package main
import "fmt"func main() {//创建一个空切片emptySlice :=make([]int,0)fmt.Println(emptySlice)//创建一个nil切片var nilSlice []intfmt.Println(nilSlice)//检查切片是否为空if len(emptySlice) == 0 {fmt.Println("emptySlice is empty")}if nilSlice == nil {fmt.Println("nilSlice is nil")}//尝试访问空切片的元素if len(emptySlice) > 0 {fmt.Println(emptySlice[0])}else {fmt.Println("emptySlice is empty")}}

•  emptySlice是一个空切片,它有长度0,但是它指向一个内存地址。
•  nilSlice是一个nil指针,它没有指向任何内存地址,因此不能用来访问任何元素。


尝试访问nil切片的元素会导致程序崩溃(panic),因为nil没有指向有效的内存。而空切片虽然长度为0,但是它是指向一个有效的内存地址的,所以可以安全地对其进行操作,比如扩展或赋值。

6.为切片中的元素赋值

对切片里某个索引指向的元素赋值和对数组里某个索引指向的元素赋值的方法完全一样。使
用 [] 操作符就可以改变某个元素的值,下面是使用切片字面量来声明切片:

// 创建一个整型切片
// 其容量和长度都是 5 个元素
myNum := []int{10, 20, 30, 40, 50}
// 改变索引为 1 的元素的值
myNum [1] = 25

切片之所以被称为切片,是因为创建一个新的切片,也就是把底层数组切出一部分。通过切片创建新切片的语法如下:

slice[i:j]
slice[i:j:k]

其中 i 表示从 slice 的第几个元素开始切,j 控制切片的长度(j-i),k 控制切片的容量(k-i),如果没有给定 k,则表示切到底层数组的最尾部。下面是几种常见的简写形式:

slice[i:]  // 从 i 切到最尾部
slice[:j]  // 从最开头切到 j(不包含 j)
slice[:]   // 从头切到尾,等价于复制整个 slice

让我们通过下面的例子来理解通过切片创建新的切片的本质:

// 创建一个整型切片
// 其长度和容量都是 5 个元素
myNum := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素
newNum := slice[1:3]

执行上面的代码后,我们有了两个切片,它们共享同一段底层数组,但通过不同的切片会看到底层数组的不同部分:

 

下面是详细的用法: 

package mainimport "fmt"func main() {// 创建一个原始切片original := []int{1, 2, 3, 4, 5}// 通过切片操作符创建新的切片a := original[:3] // 包含从索引0到索引2的元素b := original[2:] // 包含从索引2到末尾的元素c := original[:] // 创建一个新的切片,它是原始切片的一个副本// 修改原始切片original[0] = 10// 打印结果fmt.Println("Original slice:", original)  // 输出: [10 2 3 4 5]fmt.Println("Slice a:", a)               // 输出: [1 2 3]fmt.Println("Slice b:", b)               // 输出: [3 4 5]fmt.Println("Slice c:", c)               // 输出: [1 2 3 4 5]// 修改新的切片aa[0] = 100// 打印结果fmt.Println("Original slice after modifying a:", original)  // 输出: [10 2 3 4 5]fmt.Println("Slice a after modification:", a)               // 输出: [100 2 3]
}

共享底层数组的切片
需要注意的是:现在两个切片 myNum 和 newNum 共享同一个底层数组。如果一个切片修改了该底层数组的共享
部分,另一个切片也能感知到(请参考前图):

在Go语言中,切片是引用类型,这意味着它们指向底层数组的连续区域。当你通过切片操作创建一个新的切片时,新切片和原始切片可能共享同一个底层数组。如果新切片的容量足够,对新切片的修改可能会反映到原始切片上,因为它们可能指向相同的底层数组元素。

如果你需要一个与原始切片完全独立的副本,可以使用copy函数来创建一个新的底层数组。这样,对新数组的修改就不会影响原始数组。
下面是一个详细的示例,展示了如何使用copy函数创建一个完全独立的切片副本,并解释了为什么这样做可以避免对原始切片的意外修改。

package mainimport "fmt"func main() {original := []int{1,2,3,4,5}//使用切片操作符创建一个新的切片,它与原始切片共享同一个底层数组sharedSlice := original[:]independentSlice :=make([]int, len(original))copy(independentSlice, original)// 打印原始切片和两个新切片的内容fmt.Println("Original slice:", original)       // 输出: [1 2 3 4 5]fmt.Println("Shared slice:", sharedSlice)      // 输出: [1 2 3 4 5]fmt.Println("Independent slice:", independentSlice) // 输出: [1 2 3 4 5]//修改原始切片original[0] = 10fmt.Println("Modified original slice:", original) // 输出: [10 2 3 4 5]fmt.Println("Shared slice after modification:", sharedSlice) // 输出: [10 2 3 4 5]//修改独立切片independentSlice[0] = 20fmt.Println("Independent slice after modification:", independentSlice) // 输出: [20 2 3 4 5]//修改独立切片之后,原始切片不受影响fmt.Println("Original slice after independent modification:", original) // 输出: [10 2 3 4 5]fmt.Println("Shared slice after independent modification:", sharedSlice) // 输出: [10 2 3 4 5]}

代码运行结果: 

 7.切片扩容

Go 语言中的切片(slice)是一种动态数组,它允许你动态地增长和缩小。当你向切片添加元素,而切片的容量(capacity)不足以容纳更多元素时,Go 语言会自动进行扩容。扩容的具体机制如下:

  • 1.  初始容量:当你创建一个新的切片时,它会有一个初始的容量。如果你通过 make 函数创建切片,你可以指定切片的长度(length)和容量(capacity)。
  • 2.  扩容机制:当你向切片添加元素,超出当前容量时,Go 语言会进行扩容。扩容通常遵循以下规则:
  •   扩容后的容量通常是原容量的两倍,但具体增长因子可能因实现而异。
  •   如果切片的容量已经很大,扩容可能不会简单地翻倍,而是增加一个固定的数量。
  • 3.  扩容过程:扩容时,Go 语言会分配一个新的数组,并将原切片中的元素复制到新数组中。然后,新元素会被添加到新数组中。最后,切片的底层数组指针会被更新为指向新数组。
  • 4.  性能考虑:频繁的扩容可能会导致性能问题,因为每次扩容都需要分配新的内存并复制元素。为了避免这种情况,你可以通过预先分配足够的容量来减少扩容的次数。
  • 5.  手动扩容:虽然 Go 语言会自动管理切片的扩容,但你也可以手动扩容切片。例如,你可以使用 append 函数来添加元素,或者使用 copy 函数和新的切片来手动复制元素。

下面是一个简单的 Go 语言切片扩容的:

package mainimport "fmt"func main() {s := make([]int, 0, 1) // 创建一个长度为0,容量为1的切片for i := 0; i < 5; i++ {s = append(s, i)    // 向切片添加元素fmt.Printf("len=%d cap=%d slice=%v\n", len(s), cap(s), s)}
}

运行结果:

len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=3 cap=4 slice=[0 1 2]
len=4 cap=4 slice=[0 1 2 3]
len=5 cap=8 slice=[0 1 2 3 4]

函数 append() 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25%的容量(随着语言的演化,这种增长算法可能会有所改变)。

8.遍历切片

8.1使用for循环和索引

package mainimport "fmt"func main() {slice := []int{10, 20, 30, 40, 50}for i := 0; i < len(slice); i++ {fmt.Println("Element at index", i, "is", slice[i])}
}

8.2 使用for循环和range

使用range关键字可以同时获取索引和值,这是遍历切片的常用方法

package mainimport "fmt"func main() {slice :=[]int{10,20,30,40,50}for index,value := range slice{fmt.Printf("Index is %d and value is %d\n",index,value)}
}

8.3 使用 for 循环和 range(只获取值)

如果你不需要索引,只关心值,可以使用以下方式简化代码。

用以下方式简化代码。
package mainimport "fmt"func main() {slice := []int{10, 20, 30, 40, 50}for _, value := range slice {fmt.Println("Value is", value)}
}

8.4 使用 for 循环和 range(只获取索引)

如果你只需要索引,可以使用下划线 _ 忽略值。

package mainimport "fmt"func main() {slice := []int{10, 20, 30, 40, 50}for index, _ := range slice {fmt.Println("Index is", index)}
}

8.5 使用 for 循环和 range(遍历字符串切片)

对于字符串切片,range 会返回每个字符串中的每个字符(作为 rune 类型)。

package mainimport "fmt"func main() {slice := []string{"hello", "world"}for _, word := range slice {for _, char := range word {fmt.Printf("%c ", char)}fmt.Println()}
}

9.切片操作

9.1 使用copy函数

copy 函数是标准库 copy 包中提供的一个函数,用于将一个切片的内容复制到另一个切片中。它接受两个切片作为参数:源切片和目标切片,并返回复制的元素数量。

package mainimport "fmt"func main() {src :=[]int{1,2,3,4,5}dst :=make([]int,5)//从src复制到dstcopied :=copy(dst,src)fmt.Println("Copied element",copied)fmt.Println("Destination",dst)
}

9.2 使用 append 函数

  •   第一个参数是目标切片,用于接收新添加的元素。
  •  后续参数是要添加到切片中的元素。
  •   使用 append 进行切片复制
  • 在 append([]int(nil), original...) 中:
  •   []int(nil) 创建了一个类型为 []int 的空切片。这个切片的长度和容量都是0。
  •  original... 是 Go 语言的变参语法,它将 original 切片中的所有元素作为独立的参数传递给 append 函数。
package mainimport "fmt"func main() {original := []int{1, 2, 3, 4, 5}copy := append([]int(nil), original...)fmt.Println("Original slice:", original)fmt.Println("Copy slice:", copy)
}

在这个例子中,copy 切片是通过 append 函数创建的,它包含了 original 切片的所有元素。这种方法不仅代码简洁,而且性能也很好,因为它避免了不必要的内存分配和复制操作。
总结来说,append([]int(nil), original...) 是一种利用 append 函数的灵活性和智能内存管理来高效复制切片的方法。

9.3手动复制

你也可以通过遍历源切片并手动将每个元素赋值到新的切片中来实现复制。

package mainimport ("fmt"
)func main() {src := []int{1, 2, 3, 4, 5}dst := make([]int, len(src)) // 创建一个相同长度的新切片for i := range src {dst[i] = src[i]}fmt.Println("Source slice:", src)fmt.Println("Destination slice:", dst)
}

这种方法虽然直接,但效率较低,特别是对于大型切片。

注意事项

•  当使用 copy 函数时,确保目标切片有足够的容量来接收所有元素,否则它只会复制目标切片的容量允许的部分。
•  使用 append 函数时,虽然方便,但可能会因为扩容操作而增加额外的性能开销。
•  手动复制虽然控制更精细,但代码更复杂,且容易出错。
在实际应用中,选择哪种方法取决于具体的需求和性能考虑。对于大多数情况,copy 函数提供了一个简单而高效的方式来复制切片。

http://www.yayakq.cn/news/624076/

相关文章:

  • 建行网站用户名是什么贴吧高级搜索
  • wordpress+做仿站网络营销推广方案策划书
  • 百度云怎么做网站晋中网站公司
  • 泉州市住房与城乡建设网站做网站怎么添加图片
  • 南宁网站关键词推广wordpress后台首页增加论坛帖子
  • 5分钟建站wordpress秦皇岛网络推广公司
  • 汇邦团建网站谁做的南京网站群建设公司
  • 什么装修网站做的好的上海企业网站建设制作
  • 南宁网站开发培训0元入驻的电商平台
  • 捕鱼网站建设惠州关键词排名优化
  • 网站的照片上传的功能怎么做wordpress做ip统计
  • 网站建设平台官网硬件工程师需要学哪些
  • 潍坊做网站好看网站建设实施方案
  • 阿里网站官网入口人事处网站开发文献综述
  • 杭州百度推广网站建设做气体检测仪的网站
  • 上饶网站建设srsem天津重型网站建设方案公司
  • 市面上有什么搭建网站工作室东兴移动网站建设
  • 51一起做网站企业作风建设实施方案
  • 如何做网站发产品销售模板网站怎么修改
  • 给网站添加百度地图房建设计网站
  • 微网站促销版织梦网站建设选项卡教程
  • 网站建设合同免费下载吉安网站建设兼职
  • 广州建网站开发seo型企业网站wordpress适合百度吗
  • 学交互设计网站下载免费网络软件
  • 网站收录后怎么做排名自己制作网页怎么制作的
  • 沙坪坝网站建设公司选哪家好手机版网站建设合同范本
  • 做网站协议书丹东做网站
  • 电子商务网站建设摘要旅游正规免费网站建设公司
  • 最新网站推广方法百度云盘登录入口
  • 选择网站建设公司好网站的绝对路径怎么做