切片
在Golang中,数组长度是不可变的,那么我们可以自己实现简单的可变长数组。在Golang里面切片slice 里就运用了可变长数组,slice是对底层数组的抽象和控制。那么我们先看看slice的结构体
type slice struct { array unsafe.Pointer //表示任何类型的指针 len int //长度 cap int //容量 }
unsafe.Pointer 是 Go 语言中的一个特殊类型,用于表示任意类型的指针,但是它没有具体的类型信息。unsafe.Pointer
可以存储任意类型的指针值,但在使用时需要小心,因为它是不安全的,并且会绕过 Go 语言的类型系统。
- 指向底层数组的指针。(
Golang
语言是没有操作原始内存的指针的,所以unsafe
包提供相关的对内存指针的操作,一般情况下非专业人员勿用) - 切片的真正长度,也就是实际元素占用的大小。
- 切片的容量,底层固定数组的长度
每次可以初始化一个固定容量的切片,切片内部维护一个固定大小的数组。当append
新元素时,固定大小的数组不够时会自动扩容。
可变长数组 (int数组)
现在我们先开始模仿slice自定义一个可变长数组Array的结构体
// 可变长数组 type Array struct { array []int // 固定大小的数组,用满容量和满大小的切片来代替 len int // 真正长度 cap int // 容量 lock sync.Mutex // 为了并发安全使用的锁 }
//上面的array []int 是一个切片,我们把容量满的切片当成普通的数组使用,因为在golang中,数组是一定要固定大小值,即[n] int 是不被允许的,那么我们就用切片来替代
1.1新建一个可变长度的数组函数
// 新建一个可变长度的数组
func Make(len,cap int) *Array{
s:=new(Array)
if len>cap{
panic("len large than cap")
}
//把切片当数组用,array会被初始化 如果cap是5,那么appry会被初始化这样【0,0,0,0,0】
array :=make([]int, cap,cap)
//元数据
s.array=array
s.cap=cap
s.len=0
return s
}
1.2增加元素
// 增加元素,数组的容量不够时就内部重新创建新的数组,这样实现可变长数组
func (a *Array) Append(element int) {
//并发锁
a.lock.Lock()
defer a.lock.Unlock()
//len 等于cap ,表示没有多余的位置了
if a.len == a.cap {
//没容量,数组要扩容,扩容到两倍
newCap := 2 * a.len
//如果之前容量为0,那么新的容量为1
if a.cap == 0 {
newCap = 1
}
newArray := make([]int, newCap, newCap)
//把老数组的数据转移到新的数组
for k, v := range a.array {
newArray[k] = v
}
//替换数组
a.array = newArray
a.cap = newCap
}
//把元素放到数组里
a.array[a.len] = element
//真实长度+1
a.len = a.len + 1
}
1.3一次增加多个元素
//增加多个元素
func (a *Array) AppendMany(element ...int){
for _,v:=range element{
a.Append(v)
}
}
1.4获取下标的元素
//获取数组中指定下标的元素
func( a *Array) GetIndexElement(index int ) int {
//越界了
if a.len == 0 || index>=a.len {
panic("index over len")
}
return a.array[index]
}
1.5获取数组的容量
//获取数组的容量
func (a *Array) Cap() int {
return a.cap
}
1.6获取数组的长度
//获取数组的长度
func (a *Array) Len() int {
return a.len
}
测试函数(没有用到标准库中的 testing
包)
1.1测试Make函数
// 测试函数 测试Make 长度为len,容量为cap
func TestMake(len, cap int) {
// 创建一个可变长度数组,长度为 len,容量为 cap
myArray := Make(len, cap)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
}
1.2测试Append函数
// 测试Append函数
func TestAppend() {
// 创建一个可变长度数组
myArray := &Array{}
// 追加元素
myArray.Append(1)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
//len=1, cap=1, data=[1]
myArray.Append(2)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
//len=2, cap=2, data=[1,2]
myArray.Append(3)
//len=3, cap=4, data=[1,2,3,0]
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
}
main函数
func main() {
TestMake(3, 5) //结果是:Array: len=0, cap=5, data=[0 0 0]
TestAppend()
}
完整代码如下:
package main
import (
"fmt"
"sync"
)
//定义一个可变数组的长度结构
type Array struct {
array []int //固定大小数组长度,因为数组的大小一定要固定,所以不可以用[n]int ,只能用常量,这里用切片来替代
len int //真正长度
cap int //容量
lock sync.Mutex //为了并发安全使用的锁
}
// 新建一个可变长度的数组
func Make(len, cap int) *Array {
s := new(Array)
if len > cap {
panic("len large than cap")
}
//把切片当数组用
array := make([]int, len, cap)
//元数据
s.array = array
s.cap = cap
s.len = 0
return s
}
// 增加元素,数组的容量不够时就内部重新创建新的数组,这样实现可变长数组
func (a *Array) Append(element int) {
//并发锁
a.lock.Lock()
defer a.lock.Unlock()
//len 等于cap ,表示没有多余的位置了
if a.len == a.cap {
//没容量,数组要扩容,扩容到两倍
newCap := 2 * a.len
//如果之前容量为0,那么新的容量为1
if a.cap == 0 {
newCap = 1
}
newArray := make([]int, newCap, newCap)
//把老数组的数据转移到新的数组
for k, v := range a.array {
newArray[k] = v
}
//替换数组
a.array = newArray
a.cap = newCap
}
//把元素放到数组里
a.array[a.len] = element
//真实长度+1
a.len = a.len + 1
}
// 增加多个元素
func (a *Array) AppendMany(element ...int) {
for _, v := range element {
a.Append(v)
}
}
// 获取数组的容量
func (a *Array) Cap() int {
return a.cap
}
// 获取数组的长度
func (a *Array) Len() int {
return a.len
}
// 获取数组中指定下标的元素
func (a *Array) GetIndexElement(index int) int {
//越界了
if a.len == 0 || index >= a.len {
panic("index over len")
}
return a.array[index]
}
// 测试函数 测试Make 长度为len,容量为cap
func TestMake(len, cap int) {
// 创建一个可变长度数组,长度为 len,容量为 cap
myArray := Make(len, cap)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
}
// 测试Append函数
func TestAppend() {
// 创建一个可变长度数组
myArray := &Array{}
// 追加元素
myArray.Append(1)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
//len=1, cap=1, data=[1]
myArray.Append(2)
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
//len=2, cap=2, data=[1,2]
myArray.Append(3)
//len=3, cap=4, data=[1,2,3,0]
// 打印数组信息
fmt.Printf("Array: len=%d, cap=%d, data=%v\n", myArray.len, myArray.cap, myArray.array)
}
func main() {
TestMake(3, 5) //结果是:Array: len=0, cap=5, data=[0 0 0]
TestAppend()
}
总结:
golang中提供了slice这个可变长数组,利用内置函数append()来扩容。上面只是简单自定义一个int类型的可变长数组。友情提醒,在数组或者切片被创建时,如果没有赋值,系统会帮初始化
例如:array :=make([]int,4,5) 把这个输出是[0,0,0,0]