Go的数据结构与算法-----实现可变长数组

切片

在Golang中,数组长度是不可变的,那么我们可以自己实现简单的可变长数组。在Golang里面切片slice 里就运用了可变长数组,slice是对底层数组的抽象和控制。那么我们先看看slice的结构体

type slice struct {
    array unsafe.Pointer //表示任何类型的指针
    len   int //长度
    cap   int //容量
}

unsafe.Pointer 是 Go 语言中的一个特殊类型,用于表示任意类型的指针,但是它没有具体的类型信息。unsafe.Pointer 可以存储任意类型的指针值,但在使用时需要小心,因为它是不安全的,并且会绕过 Go 语言的类型系统。

  1. 指向底层数组的指针。(Golang语言是没有操作原始内存的指针的,所以unsafe包提供相关的对内存指针的操作,一般情况下非专业人员勿用)
  2. 切片的真正长度,也就是实际元素占用的大小。
  3. 切片的容量,底层固定数组的长度

每次可以初始化一个固定容量的切片,切片内部维护一个固定大小的数组。当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]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值