高效GO语言编程(7)——切片

欢迎加入GolangRoadmap,一个年轻的GO开发者社区https://www.golangroadmap.com/,目前是邀请制注册,注册码:Gopher-1035-0722,已开放GO内推,GO面试,GO宝典,GO返利等栏目

切片

切片通过对数组进行封装,为数据序列提供了更通用、强大而方便的接口。 除了矩阵变换这类需要明确维度的情况外,Go中的大部分数组编程都是通过切片来完成的。

切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。 若某个函数将一个切片作为参数传入,则它对该切片元素的修改对调用者而言同样可见, 这可以理解为传递了底层数组的指针。因此,Read 函数可接受一个切片实参 而非一个指针和一个计数;切片的长度决定了可读取数据的上限。以下为 os 包中 File 类型的 Read 方法签名:

func (f *File) Read(buf []byte) (n int, err error)

该方法返回读取的字节数和一个错误值(若有的话)。若要从更大的缓冲区 b 中读取前32个字节,只需对其进行切片即可。

    n, err := f.Read(buf[0:32])

这种切片的方法常用且高效。若不谈效率,以下片段同样能读取该缓冲区的前32个字节。

    var n int
    var err error
    for i := 0; i < 32; i++ {
        nbytes, e := f.Read(buf[i:i+1])  // Read one byte.
        n += nbytes
        if nbytes == 0 || e != nil {
            err = e
            break
        }
    }

只要切片不超出底层数组的限制,它的长度就是可变的,只需将它赋予其自身的切片即可。 切片的容量可通过内建函数 cap 获得,它将给出该切片可取得的最大长度。 以下是将数据追加到切片的函数。若数据超出其容量,则会重新分配该切片。返回值即为所得的切片。 该函数中所使用的 len 和 cap 在应用于 nil 切片时是合法的,它会返回 0。

func Append(slice, data []byte) []byte {
    l := len(slice)
    if l + len(data) > cap(slice) {  // 重新分配
        // 为未来的增长,双重分配所需的内容.
        newSlice := make([]byte, (l+len(data))*2)
        // copy函数是预先声明的,适用于任何切片类型。
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:l+len(data)]
    copy(slice[l:], data)
    return slice
}

最终我们必须返回切片,因为尽管 Append 可修改 slice 的元素,但切片自身(其运行时数据结构包含指针、长度和容量)是通过值传递的。

向切片追加东西的想法非常有用,因此有专门的内建函数 append。 要理解该函数的设计,我们还需要一些额外的信息,我们将稍后再介绍它。

二维切片

Go的数组和切片都是一维的。要创建等价的二维数组或切片,就必须定义一个数组的数组, 或切片的切片,就像这样:

type Transform [3][3]float64  // 一个 3x3 的数组,其实是包含多个数组的一个数组。
type LinesOfText [][]byte     // 包含多个字节切片的一个切片。

由于切片长度是可变的,因此其内部可能拥有多个不同长度的切片。在我们的 LinesOfText 例子中,这是种常见的情况:每行都有其自己的长度。

text := LinesOfText{
	[]byte("Now is the time"),
	[]byte("for all good gophers"),
	[]byte("to bring some fun to the party."),
}

有时必须分配一个二维数组,例如在处理像素的扫描行时,这种情况就会发生。 我们有两种方式来达到这个目的。一种就是独立地分配每一个切片;而另一种就是只分配一个数组, 将各个切片都指向它。采用哪种方式取决于你的应用。若切片会增长或收缩, 就应该通过独立分配来避免覆盖下一行;若不会,用单次分配来构造对象会更加高效。 以下是这两种方法的大概代码,仅供参考。首先是一次一行的:

// 分配底层切片.
picture := make([][]uint8, YSize) // y每一行的大小
//循环遍历每一行
for i := range picture {
	picture[i] = make([]uint8, XSize)
}

现在是一次分配,对行进行切片:

// 分配底层切片
picture := make([][]uint8, YSize) //  每 y 个单元一行。
// 分配一个大一些的切片以容纳所有的元素
pixels := make([]uint8, XSize*YSize) // 指定类型[]uint8, 即便图片是 [][]uint8.
//循环遍历图片所有行,从剩余像素切片的前面对每一行进行切片。
for i := range picture {
	picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值