Go_切片(初始化、遍历、截取、修改、append、copy、切片作为函数参数、切片求和、切片求最大值)

切片:

  • 切片的长度是不固定的,可以追加数据,可以理解是一个动态数组,切片的底层是一个结构体
  • 切片类型(slice)本身并不是动态数组或数组指针。它内部通过指针引用底层数组,设定相关属性将操作限定在指定范围内。当需要时,会申请更大的内存,将当前数据复制过去, 以实现类似动态数组的功能。
type slice struct { array unsafe.Pointer //  指针,指向底层数组len int // 切片的长度cap int // 切片的容量
}

怎么区分数组和切片:

数组是必须要有长度,不管是显式还是推导都要有长度,但是切片不是一定要显式长度

切片的创建:

可直接创建切片对象,无需预先准备数组。因为是引用类型,必须使用make函数或显式初始化语句,它会自动完成底层数组内存分配

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

在这里插入图片描述

普通格式:

var slice [] type

自动推导类型创建切片:

slice := [] type{}

make函数创建切片:

// len:切片的长度    cap:切片的容量    cap不能大于len,且可省略,默认和len相同
// len和cap:长度是已经初始化的空间,容量是已经开辟的空间,包括已经初始化的空间和空闲的空间
// cap是一个内置函数,用于统计切片的容量,也就是最大可以存放多少个元素
// 仅定义没有写make就相当于创建了切片里面是空的var slice []type = make([]type, lencap)
var slice = make([]type, len, cap) // 简写

演示:

func main() {var slice []int             // 普通格式创建的slice,空切片slice2 := []int{}           // 自动推导类型创建的slice,空切片slice3 := make([]int, 2)    // make函数创建的slice,省略cap,cap=lenslice4 := make([]int, 2, 3) // make函数创建的slice,自定义capfmt.Println("slice:", slice)fmt.Println("slice2:", slice2)fmt.Println("slice3:", slice3, "len:", len(slice3), "cap:", cap(slice3))fmt.Println("slice4:", slice4, "len:", len(slice4), "cap:", cap(slice4))
}

输出:

slice:  []
slice2: []
slice3: [0 0]  len: 2  cap: 2
slice4: [0 0]  len: 2  cap: 3

引用数组创建:

func main() {// 1. 创建数组arr := [...]int{1, 2, 3, 4, 5}// 2.创建切片并引用数组// slice:切片名// arr[1:3]:引用了数组arr的数据,从索引1到索引3-1(包含索引1但是不包含索引3,也就是arr[2,3])// slice := arr// 相当于引用数组全部数据slice := arr[1:3]fmt.Println("arr:", arr)// 现在切片中只有 [2 3],也就是索引0和1,如果用取slice[2]就会越界fmt.Println("slice:", slice)//fmt.Println("slice:", slice[2]) // 报错fmt.Println("slice中元素的个数:", len(slice))fmt.Println("slice的容量:", cap(slice))
}

输出:

arr: [1 2 3 4 5]
slice: [2 3]
slice中元素的个数: 2
slice的容量: 4

引用数组或切片会同步数据

func main() {slice := make([]int, 2, 3)slice[0] = 1slice[1] = 2slice2 := slice[0:]slice2[0] = 100fmt.Println(slice)fmt.Println(slice2)
}

输出:

[100 2]
[100 2]

引用数组简写格式:

slice := arr[0:indexEnd]   简写为   slice := arr[:indexEnd]
slice := arr[indexStart:len(arr)]   简写为    slice := arr[indexStart:]
slice := arr[0:len(arr)]   简写为   slice := arr[:]  或  slice := arr

初始化:

三种创建格式,都是可以通过append向切片添加数据的

初始化格式:

/// 普通格式创建的切片
var slice []type// 自动推导类型创建的切片,这种情况会根据添加的数据推断切片的长度,容量=长度
slice := [] type{数据1,数据2,数据3}// make函数方式创建的切片可以通过append和循环初始化
slice = append(slice, 数据1,数据2...)

演示:

func main() {// var slice []typevar slice []intslice = append(slice, 1, 2, 3, 4) // 通过append添加slice[1] = 111                    // 通过索引添加fmt.Println("切片中的数据", slice)fmt.Println("可以通过索引取部分数据", slice[0])fmt.Println("切片长度:", len(slice), "切片的容量", cap(slice))fmt.Println()// slice := [] type{数据1,数据2,数据3}slice2 := []int{1, 2, 3, 4, 5}slice2 = append(slice2, 6, 7, 8, 9, 10)slice2[1] = 111fmt.Println("切片中的数据", slice2)fmt.Println("可以通过索引取部分数据", slice2[0])fmt.Println("切片长度:", len(slice2), "切片的容量", cap(slice2))fmt.Println()// slice = append(slice, 数据1,数据2...)slice3 := make([]int, 3, 4)for i := 0; i < len(slice3); i++ {slice3[i] = i + 1}slice3 = append(slice3, 1)fmt.Println("切片中的数据", slice3)fmt.Println("可以通过索引取部分数据", slice3[0])fmt.Println("切片长度:", len(slice3), "切片容量:", cap(slice3))
}

输出:

切片中的数据 [1 111 3 4]
可以通过索引取部分数据 1
切片长度: 4 切片的容量 4切片中的数据 [1 111 3 4 5 6 7 8 9 10]
可以通过索引取部分数据 1
切片长度: 10 切片的容量 10切片中的数据 [1 2 3 1]
可以通过索引取部分数据 1
切片长度: 4 切片容量: 4

注意下面两种定义方式的区别。前者仅定义了一个[ ]int类型变量,并未执行初始化操作,而后者则用初始化表达式完成了全部创建过程

func main() {var a []intb := []int{}fmt.Println(a == nil, b == nil) // true false
}

可获取元素地址,但不能向数组那样直接通过指针(*slice)访问元素内容

func main() {s := []int{0, 1, 2, 3, 4}p := &s     // 取切片地址p0 := &s[0] // 取第一个元素地址p1 := &s[1]println(p, p0, p1)// 取p中[1]的数据且加100(*p)[1] += 100 // *[]int不支持indexing操作,须先用指针操作符获取[]int对象fmt.Println(s)fmt.Println((*p)[1]) //这里加括号是优先级问题
}

如果元素类型也是切片,那么就能实现类似交错数组的功能

func main() {x := [][]int{{1, 2},{10, 20, 30},{100},}fmt.Println(x[1])x[2] = append(x[2], 200, 300)fmt.Println(x[2])
}

输出:

[1 2] [10 20 30]
[100 200 300]

切片遍历:

遍历和数组一样可以使用普通的for循环和range遍历得到

func main() {slice := []int{1, 2, 3, 4, 5}for i := 0; i < len(slice); i++ {fmt.Print(slice[i])}for _, v := range slice {fmt.Println(v)}
}

append函数:

append函数是向切片的末尾添加数据
如果添加的内容超出了切片初始定义的容量,切片会自动扩容
扩容机制是:上一次的容量 * 2如果超过1024字节,每次扩容上一次的1/4
append每次扩容都是一个新的内存,和原来的无关联,所以如果是通过参数传递的方式,使用append添加数据,但是不会影响到原切片的数据,原因就是append每次拓展都是一个新的空间,指向的内存不再是原切片。

切片append底层原理:

  1. 创建新的数组并扩容
  2. 将原数组的数据拷贝到新数组
  3. 将新添加的数据添加到新数组
  4. slice引用到新数组
  5. 原数组被程序销毁

格式:

slice = append(slice,数据) 

演示:

func main() {slice := make([]int, 3, 4)// 这里定义了切片的长度是3,初始容量是4,系统会对长度赋上默认值,int类型就是0,所以打印3个0fmt.Println("初始切片的数据:", slice, "长度:", len(slice))slice = append(slice, 1)// 添加数据,此时容量是4,数据已经有4个了,如果继续多加点数据,会不会报错fmt.Println("第一次添加数据:", slice, "长度:", len(slice))slice = append(slice, 2, 3, 4)fmt.Println("第二次添加数据:", slice, "长度:", len(slice))// 这里是相当于把自己的数据再添加一遍slice = append(slice, slice...)fmt.Println("第三次添加数据:", slice, "长度:", len(slice))// 再创建一个切片,初始化后把slice的数据添加过来,注意,这个不是复制,是在结尾继续添加slice2 := make([]int, 3, 4)slice2 = append(slice2, slice...)fmt.Println("slice2:", slice2, "长度:", len(slice2))
}

输出:

初始切片的数据: [0 0 0]  长度: 3
第一次添加数据: [0 0 0 1]  长度: 4
第二次添加数据: [0 0 0 1 2 3 4]  长度: 7
第三次添加数据: [0 0 0 1 2 3 4 0 0 0 1 2 3 4]  长度: 14
slice2:       [0 0 0 0 0 0 1 2 3 4 0 0 0 1 2 3 4]  长度: 17

copy函数:

切片是引用类型,新切片复制后,修改数据是不会影响原切片的
注意:如果切片1的容量不够,则不赋值剩余的数据。如果切片1的数据比切片2的多,从切片2复制的数据是有多少,复制多少。
总结:copy只是复制索引相对应的数据,如果长度不够,不会覆盖原来的数据

格式:

copy(切片1,切片2) // 把切片2的数据复制到切片1中

从切片2复制到切片1,但是切片2的数据比切片1的多,所以,最终只是复制了一部分,也就是索引相对应的数据

func main() {slice := []int{1, 2, 3}slice2 := []int{4, 5, 6, 7, 8, 9}copy(slice, slice2) // 把切片2复制到切片1fmt.Println(slice)  // [4 5 6]// 修改数据slice = append(slice, 97, 98)slice2 = append(slice2, 100)fmt.Println(slice)  // [4 5 6 97 98]fmt.Println(slice2) // [4 5 6 7 8 9 100]
}

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

func main() {b := make([]byte, 3)s := copy(b, "abcde") // 字符串复制数据到[]bytefmt.Println(s) // 3fmt.Println(b) // [97 98 99]
}

切片截取:

切片截取就是从切片中获取指定的数据返回给新切片
如果初始化切片时,没有指定切片的容量,切片容量是跟随原切片的

切片截取的操作:

操作含义
s[n]从切片s的n索引到len(s)-1
s[:]从切片s的索引位置0到len(s)-1处所获得的切片
s[low:]从切片s的索引位置low到len(s)-1处所获得的切片
s[:high]从切片s的索引位置0到high处所获得的切片,len=high
s[low:high]从切片s的索引位置low到high处所获得的切片,len=high-low
s[low:high:max]从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low
len(s)切片s的长度,总是<=cap(s)
cap(s)切片s的容量,总是>=len(s)

在这里插入图片描述

func main() {slice := []int{1, 2, 3, 4, 5}/**第一个值:截取的起始索引第二个值:截取的终止索引(不包括该值)第三个值:用来计算切片的容量,可以省略,默认和长度一样容量 = 第三个值 - 第一个值长度 = 第二个值 - 第一个值*/newSlice := slice[0:3:3]fmt.Println("newSlice:", newSlice, "长度:", len(newSlice), "容量:", cap(newSlice))// 和复制一样了newSlice2 := slice[:]fmt.Println("newSlice2:", newSlice2, "长度:", len(newSlice2), "容量:", cap(newSlice2))
}

输出:

newSlice: [1 2 3] 长度: 3 容量: 3
newSlice2: [1 2 3 4 5] 长度: 5 容量: 5
切片值的修改:

切片截取后返回新切片,对新切片的值进行修改,会影响原来的切片

原因:

切片截取后新的切片是指向了原来的切片,没有给新的切片开辟新的空间,所以对于新的切片操作会影响到原来的切片

演示:

func main() {slice := []int{1, 2, 3, 4, 5}newSlice2 := slice[0:3]fmt.Println("切片修改前slice的数据:", slice)newSlice2[0] = 1111fmt.Println("切片修改后slice的数据:", slice)
}

输出:

切片修改前slice的数据: [1 2 3 4 5]
切片修改后slice的数据: [1111 2 3 4 5]

切片作为函数参数:

切片可以做为函数的参数,但是在函数中修改切片的值,会影响到原切片

因为切片的底层是结构体,结构体里有个参数Pointer,Pointer会指向切片的内存地址,使用的是浅拷贝方式,所以会影响到原切片值

演示

func main() {slice := make([]int, 10)SliceDemo(slice)fmt.Println(slice) // [0 1 2 3 4 5 6 7 8 9]}func SliceDemo(slice []int) {for i := 0; i < len(slice); i++ {slice[i] = i}
}

使用append就不会影响,而且内存地址都不一样怎么会影响呢?因为append会变成一个新的切片

func main() {slice := []int{1, 2, 3, 4, 5}SliceDemo(slice)fmt.Println("main:", slice)fmt.Printf("main:%p", slice)
}func SliceDemo(slice []int) {slice = append(slice, 6, 7)fmt.Println("SliceDemo:", slice)fmt.Printf("SliceDemo:%p\n", slice)
}

输出:

SliceDemo: [1 2 3 4 5 6 7]
SliceDemo:0x1400001c0a0
main: [1 2 3 4 5]
main:0x14000014180

去掉append,内存地址就一样了

func main() {slice := []int{1, 2, 3, 4, 5}SliceDemo(slice)fmt.Println("main:", slice)fmt.Printf("main:%p", slice)
}func SliceDemo(slice []int) {fmt.Println("SliceDemo:", slice)fmt.Printf("SliceDemo:%p\n", slice)
}

输出:

SliceDemo: [1 2 3 4 5]
SliceDemo:0x14000014180
main: [1 2 3 4 5]
main:0x14000014180

切片求和:

func main() {// 定义变量,并收集用户输入的个数var count intfmt.Println("请输入要求和的个数:")fmt.Scan(&count)// 定义切片,将输入的个数保存到切片slice := make([]int, count)statisticalData(slice)// 求和summation(slice)
}func statisticalData(slice []int) {for i := 0; i < len(slice); i++ {fmt.Printf("请输入第%d个数\n", i+1)fmt.Scan(&slice[i])}
}
func summation(slice []int) {var sum intfor i := 0; i < len(slice); i++ {sum += slice[i]}fmt.Println("和为:", sum)
}

切片求最大值:

func main() {// 定义变量,并收集用户输入的个数var count intfmt.Println("请输入要比较的数:")fmt.Scan(&count)// 定义切片,将输入的个数保存到切片slice := make([]int, count)statisticalData(slice)// 比较最大值maximum(slice)
}func statisticalData(slice []int) {for i := 0; i < len(slice); i++ {fmt.Printf("请输入第%d个数\n", i+1)fmt.Scan(&slice[i])}
}func maximum(slice []int) {max := slice[0]for i := 0; i < len(slice); i++ {if max < slice[i] {max = slice[i]}}fmt.Println("最大值是:", max)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/572116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴Java开发手册——速读记录

本随笔基于阿里巴巴Java开发手册V1.2&#xff0c;陆陆续续记录一些现阶段能理解的&#xff0c;有启发的内容&#xff0c;并将持续更新 最佳实践——插件使用已经发布为随笔&#xff01;http://www.cnblogs.com/jiangbei/p/7668654.html 一、编程规范 1.命名规范 &#xff08;1&…

Go_指针的使用、数组指针和指针数组、指针与切片、指针与结构体、多级指针

指针&#xff1a; 指针是一个特殊的变量&#xff0c;因为它存储的数据是另一个变量的内存地址&#xff0c;指针本身也是有内存地址的指针的数据类型有int、float、bool、string、数组、结构体指针的作用就是可以通过变量/对象的内存地址去操作变量/对象 注意&#xff1a; 取址运…

Go_面向对象(抽象、封装、继承)

抽象 抽象是一种编程思维方式&#xff0c;是从多个事物中提取共性 例&#xff1a;产品经理和程序员都有工作的方法&#xff0c;但是工作内容不同&#xff0c;可以把工作抽象出来定义为一个方法&#xff0c;具体细节由调用者补充 银行存取款案例&#xff1a; 账号结构体取款方法…

Discrete Logging POJ - 2417(BSGS)

Discrete Logging POJ - 2417 题意&#xff1a;给P&#xff0c;B&#xff0c;N&#xff0c;求最小的L使得 BL≡N (mod P)&#xff0c;其中P是素数。 Baby Step Giant Step 1 #include <cstdio>2 #include <cstring>3 #include <iostream>4 #include <cma…

js 根据固定位置获取经纬度--腾讯地图

1.首先引入jq 和 腾讯地图js <script src"../js/jQuery.js"></script> <script charset"utf-8" src"http://map.qq.com/api/js?v2.exp"></script> 2.html代码部分 <body onload"init()"><button ty…

Golang——string字符串常用函数(Contains、join、Index、Repeat、Replace、Split、Trim、Fields)

更多的还是去官方文档里去看&#xff1a;https://studygolang.com/pkgdoc Contains&#xff1a; 判断字符串中是否包含指定字符串 演示&#xff1a; func main() {str1 : "itzhuzhu"result : strings.Contains(str1, "zhu")fmt.Println(result) }join&a…

[flask 优化] 由flask-bootstrap,flask-moment引起的访问速度慢的原因及解决办法

一周时间快速阅读了400页的《javascript基础教程》&#xff0c;理解了主要概念。解决了一个很久之前的疑问。 我的网站是使用flask框架搭建的&#xff0c;介绍flask web的一本著名的书&#xff08;之前提到过&#xff09;作者搭建个人博客时&#xff0c;向读者推荐了flask-boot…

Go_关键字、编译、转义字符

关键字&#xff1a; 关键字是指被go语言赋予了特殊含义的单词&#xff0c;共25个&#xff0c;关键字不能用于自定义名字&#xff0c;只能在特定语法结构中使用。 breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypec…

并发编程概念、程序线程进程、线程同步、互斥量、读写锁、协程并发

多线程&#xff1a; 多线程就是同时执行多个应用程序&#xff0c;需要硬件的支持同时执行&#xff1a;不是某个时间段同时&#xff0c;cpu切换的比较快&#xff0c;所有用户会感觉是在同时运行 并发与并行&#xff1a; 并行(parallel)&#xff1a;指在同一时刻&#xff0c;有多…

第4阶段——制作根文件系统之分析init_post()如何启动第1个程序(1)

本章学习如何启动第一个应用程序 1.在前面的分析中我们了解到&#xff0c;在init进程中内核挂接到根文件系统之后&#xff0c;会开始启动第一个应用程序: kernel_init函数代码如下: static int __init kernel_init(void * unused) //进入init进程 …

Golang并发——并发技术Goroutine和channel的使用、定时器、生产者消费者、条件变量、select

Goroutine: goroutine是Go并行设计的核心。goroutine说到底其实就是协程&#xff0c;它比线程更小&#xff0c;十几个goroutine可能体现在底层就是五六个线程&#xff0c;Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB)&#x…

Oozie协作框架

Oozie协作框架 一&#xff1a;概述 1.大数据协作框架 2.Hadoop的任务调度 3.Oozie的三大功能 Oozie Workflow jobs Oozie Coordinator jobs Oozie Bundle 4.Oozie的架构 控制流节点 起始&#xff0c;分支&#xff0c;并发&#xff0c;汇合&#xff0c;结束 动作节点action 5.O…

11.4 专利法与反不正当竞争法解读

第六条是对于职务作品的一个定性.它这个职务作品、职务发明创造和我们前面著作法所讲到的职务作品的处理方式基本一致.就是如果认定某一个作品它是属于职务作品、职务发明创造,那么这一个作品它的专利权应该是属于单位而不是个人.只有认定这个创造为非职务发明创造的时候,申请的…

一文入门网络编程:常见协议、通信过程、Socket、CS/BS、TCP/UDP

网络编程三要素&#xff1a;ip地址、端口、协议&#xff0c;在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;可以进行数据传输 常见协议&#xff1a; 传输层 常见协议有TCP/UDP协议。应用层 常见的协议有HTTP协议&#xff0c;FTP协议。网络层 常见协议有IP协议…

【Linux笔记(000) 】-- 系统启动过程

索引&#xff1a; 目录索引 一. 启动流程 BIOS --> MBR(Boot Code) --> 引导程序(GRUB) --> 加载内核 --> 执行Init --> runlevel 二. 内容详解 BIOS: Basic Input Output System , 基本输入输出系统 ,负责检查硬件,查找启动设备, 可启动设备在BIOS中定义。…

Golang——TCP、UDP实现并发(服务端与客户端)

Server端常用函数、接口&#xff1a; Listen函数&#xff1a;func Listen(network, address string) (Listener, error)network&#xff1a;选用的协议&#xff1a;TCP、UDP&#xff0c; 如&#xff1a;“tcp”或 “udp”address&#xff1a;IP地址端口号, 如&#xff1a;…

java中 将字符串时间 '2015-9-8 17:05:06' 转化为格式 '2015-09-08 17:05:06'

/** * 将字符串时间2015-9-8 17:05:06转化为格式2015-09-08 17:05:06 */import java.text.SimpleDateFormat; public class TestDate{ public static void main(String[] args) throws Exception{ String time "2015-9-8 17:05:06";//注意&#xff1a;时分秒必须都…

详解TCP协议三次握手四次挥手

三次握手&#xff1a; 三次握手表示建立通信阶段&#xff0c;在TCP协议中&#xff0c;在发送数据的准备阶段&#xff0c;客户端与服务器之间的三次交互&#xff0c;以保证连接的可靠&#xff0c;由于这种面向连接的特性&#xff0c; TCP协议可以保证传输数据的安全&#xff0c;…

Golang——实现文件传输

借助TCP完成文件的传输&#xff0c;基本思路如下&#xff1a; 发送方&#xff08;客户端&#xff09;向服务端发送文件名&#xff0c;服务端保存该文件名。接收方&#xff08;服务端&#xff09;向客户端返回一个消息ok&#xff0c;确认文件名保存成功。发送方&#xff08;客户…

Golang——HTTP编程请求和响应实现

请求&#xff1a; HTTP 请求报文由请求行、请求头部、空行、请求包体4个部分组成&#xff0c;如下图所示&#xff1a; 请求行&#xff1a; 请求行由方法字段、URL 字段 和HTTP 协议版本字段 3个部分组成&#xff0c;他们之间使用空格隔开。常用的 HTTP 请求方法有 GET、POST。…