### append
向原切片末尾添加元素,并返回新的切片对象.
~~~
s := []int{}
fmt.Println(s, len(s), cap(s))
s = append(s, 1)
s = append(s, 2)
fmt.Println(s, len(s), cap(s))
s1 :=[]int{1,2,3}
s1 = append(s1,100)
fmt.Println(s1)
~~~
~~~
[] 0 0
[1 2] 2 2
[1 2 3 100]
~~~
### append扩容特点
函数 append 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是
会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次
增加 25%的容量。随着语言的演化,这种增长算法可能会有所改变
~~~
s := make([]int, 0, 1)
fmt.Println(s, len(s), cap(s))
oldCap := cap(s)
for i := 0; i < 17; i++ {
s = append(s, i)
if newCap := cap(s); oldCap < newCap {
fmt.Println("当前长度", len(s),"新容量", newCap)
}
}
~~~
~~~
[] 0 1
当前长度 2 新容量 2
当前长度 3 新容量 4
当前长度 4 新容量 4
当前长度 5 新容量 8
当前长度 6 新容量 8
当前长度 7 新容量 8
当前长度 8 新容量 8
当前长度 9 新容量 16
当前长度 10 新容量 16
当前长度 11 新容量 16
当前长度 12 新容量 16
当前长度 13 新容量 16
当前长度 14 新容量 16
当前长度 15 新容量 16
当前长度 16 新容量 16
当前长度 17 新容量 32
~~~
**注意: 使用append向容量也使用了底层数组的切片添加元素也会改变底层数组的值**
~~~
slice := []int{10, 20, 30, 40, 50}
newSlice := slice[1:3]
fmt.Println(newSlice, len(newSlice), cap(newSlice))
newSlice = append(newSlice, 60)
fmt.Println(newSlice, len(newSlice), cap(newSlice))
fmt.Println(slice, len(slice), cap(slice))
~~~
~~~
[20 30] 2 4
[20 30 60] 3 4
[10 20 30 60 50] 5 5
~~~
### 创建切片时的第三个索引
~~~
在创建切片时,还可以使用之前我们没有提及的第三个索引选项。第三个索引可以用来控制新
切片的容量。其目的并不是要增加容量,而是要限制容量。可以看到,允许限制新切片的容量
为底层数组提供了一定的保护,可以更好地控制追加操作. *如果在创建切片时设置切片的容量
和长度一样,就可以强制让新切片的第一个 append 操作创建新的底层数组,与原有的底层数
组分离。新切片与原有的底层数组分离后,可以安全地进行后续修改.*
~~~
~~~
slice := []int{10, 20, 30, 40, 50}
newSlice := slice[1:3:3]
fmt.Println(newSlice, len(newSlice), cap(newSlice))
newSlice = append(newSlice, 100)
fmt.Println(slice, len(slice), cap(slice))
fmt.Println(newSlice, len(newSlice), cap(newSlice))
~~~
**因为限制了容量,所以没有对底层数组的元素也进行修改**
~~~
[20 30] 2 2
[10 20 30 40 50] 5 5
[20 30 100] 3 4
~~~
### ...运算符
使用...运算符可以将一个切片的所有元素添加到另一个元素当中
~~~
s1 := []int{10, 20, 30, 40, 50}
s2 := []int{90, 100}
fmt.Println(append(s1, s2...))
~~~
~~~
[10 20 30 40 50 90 100]
~~~
### copy
~~~
s1 := []int{1, 2, 3}
s2 := []int{4, 5, 6, 7, 8, 9}
copy(s2, s1) //前面是目标切片
fmt.Println(s2)
~~~
~~~
[1 2 3 7 8 9]
~~~
- 基本语法
- 申明变量
- 常量
- 数据类型
- 强制类型转换
- 获取命令行参数
- 指针
- 概述
- new函数
- 函数
- 概述
- 不定参数类型
- 有返回值
- 函数类型
- 回调函数
- 匿名函数和闭包
- 延迟调用defer
- 工程管理
- 工作区
- src,pkg和bin目录
- 复合类型
- 概述
- 数组
- 概述
- 声明并初始化
- 拷贝传值
- slice
- 概述
- 创建切片
- 切片截取
- 切片和底层数组的关系
- slice常用方法
- 切片做函数参数
- map
- 概述
- map操作
- 结构体
- 概述
- 结构体初始化
- 结构体比较
- 结构体作为函数参数
- 结构体前加&
- 面向对象
- 概述
- 匿名组合
- 方法
- 值语义和引用语义
- 方法集
- 方法的继承
- 方法重写
- 方法值
- 接口
- 接口定义和实现
- 多态的表现
- 接口继承
- 接口转换
- 空接口
- 类型断言
- 异常处理
- error接口
- panic
- recover
- 文本文件处理
- 字符串操作
- 正则表达式
- json处理
- 文件操作
- 标准设备文件操作
- 并发编程
- 概述
- 并发和并行
- go语言并发优势
- goroutine
- goroutine概述
- 创建goroutine
- 主协程先退出
- runtime包
- Gosched
- Goexit
- GOMAXPROCE
- channel
- 多资源竞争
- channel类型
- 无缓冲channel
- 有缓冲channel
- 关闭channel
- 单向channel
- 单向channel特性
- 定时器
- Timer
- Ticker
- select
- select作用
- 超时
- sync
- 竞争状态
- 网络编程
- 网络概述
- 网络协议
- 分层模型
- 网络分层架构
- 层与协议
- 每层协议的功能
- 链路层
- 网络层
- 传输层
- 应用层
- socket编程
- 组合和继承
- 注意事项
- 细节
- go语言实现队列
- google工程师golang
- 基础语法
- 内建容器
- 面向"对象"
- 依赖管理
- 面向接口
- 函数式编程
- 错误处理和资源管理
- 测试与性能调优
- goroutine
- channel
- golang问题集
- 断言和类型转换
- Go语言圣经
- 入门
- 程序结构
- 命名
- 声明
- 变量
- 赋值
- 类型
- 包和文件
- 作用域
- 基础数据类型
- 整数
- 浮点数
- 复数
- 布尔型
- 字符串
- 常量
- 复合数据类型
- 数组
- slice
- map
- 结构体
- json
- 文本和HTML模板
- 函数
- 函数声明
- 错误
- 函数值
- 匿名函数
- defer
- panic
- recover
- 方法
- 方法声明
- 指针对象的方法
- 封装
- 接口
- 说明
- 接口是合约
- 实现接口的条件
- 接口值
- 类型断言
- 通过类型断言询问行为
- 类型开关
- Goroutines和Channels
- 协程
- channels
- 无缓冲channel
- 串联的channel
- 有缓冲channel
- 并发的循环
- select多路复用
- 并发的退出
- 并发问题的自我思考
- 基于共享变量的并发
- 竞争条件
- 互斥锁
- 读写锁
- 内存同步
- sync.Once
- 协程和线程
- 包和工具
- 测试
- 反射
- 什么是反射
- 为什么需要反射
- reflect.Type和reflect.Value
- 通过reflect.Value修改值
- 底层编程
