切片的高效操作

扩展切片
  • append 在切片开头添加元素, append 的本质是用于追加元素而不是扩展容量, 扩展切片的容量只是 append 的一个副作用

       var a = []int{1,2,3}
       a = append([]int{0}, a...)
    

    在开头一般都会导致内存的重新分配,而且会导致已有的元素全部复制 1 次
    因此,从切片的开头添加元素的性能一般要比从尾部追加元素的性能差很多

  • append 函数返回新的切片, 也就是说它支持链式操作

       var a []int
       // 在 a[i] 的位置添加一个元素 原先后面的元素往后移
       a = append(a[:i], append([]int{x}, a[i:]...)...)
    

    append 调用都会创建一个临时切片, 可以用 copy 和 append 组合可以避免创建
    中间的临时切片, 同样是完成添加元素的操作

       a = append(a, 0) // 切片扩展一个空间
       copy(a[i+1:], a[i:]) // a[i:] 向后移动 1 个位置
       a[i] = x // 设置新添加的元素
    
删除切片元素
  • 删除元素的位置有三种情况: 从开头位置删除, 从中间位置删除, 从尾部删除。 其中删除切片尾部的元素最快:
    • 删除尾部元素
        a := []int{1,2,3}
        a = a[:len(a)-1] // 删除尾部 1 个元素
        a = a[:len(a)-N] // 删除尾部 N 个元素
      
    • 删除开头可以直接移动数据指针
        a = a[1:]
        a = a[N:]
        // 也可以不移动指针 用 append 在原地完成
        // 所谓的原地完成是指在原有的切片数据对应的内存区间内完成, 不会导致内存空间结构的变化
        a = append(a[:0], a[1:]...)
        a = append(a[:0], a[N:]...)
        // 也可以用 copy 完成删除开头的元素
        // copy函数把后面的几个元素覆盖前面的元素
        a = a[:copy(a, a[1:])]
      
    • 删除中间元素: 需要对剩余的元素进行一次整体挪动, 同样可以用 append 或 copy 原地完成:
        a = append(a[:i], a[i+1:]...)
        a = a[:i+copy(a[i:], a[i+1:])]
      
切片内存技巧

对于切片来说, len 为 0,但是 cap 不为 0 的切片则是非常有用的特性。

  • 下面函数利用了 0长切片的特性, 实现高效而且简洁

       func TrimSpace(s []byte) []byte {
    	    b := s[:0]
            for _, x := range s {
       	        if x != ' ' {
    	        	b = append(b,x)
    	        }
           }
       }
    
  • 切片高效操作的要点是要降低内存分配的次数, 尽量保证 append 操作不会超出 cap 的容量, 降低触发内存分配的次数和每次分配内存的大小

  • 切片内存泄漏: 切片操作并不会复制底层的数据. 底层的数组会被保存在内存中, 直到它不再被引用
    但有时候可能会因为一个小的内存引用而导致底层整个数组处于被使用的状态, 这会延迟自动内存回收器对底层数组的回收

    func FindPhoneNumber(filename string) []byte {
       b, _ ;= ioutil.ReadFile(filename)
       return regexp.MustCompile(`[0-9]+`).Find(b)
    }
    

    这段代码返回的 []byte 指向保存整个文件的数组。 因为切片引用了整个原始数组, 导致自动垃圾回收器不能及时释放底层数组的空间
    一个小的需求可能导致需要长时间保存整个文件数据。虽然这并不是传统意义上的内存泄漏,但是可能会拖慢系统的整体性能
    修复这个问题 可以将感兴趣的数据复制到一个新的切片中(虽然传值有一定的代价, 但是换取的好处是切断了对原始数据的依赖)

    return append([]byte{}, b...)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值