实现一个支持动态扩容的数组


主要考虑3个问题

  1. 主要的操作
  2. 扩容的策略
  3. 数据迁移策略

其中,主要操作如下:

PS. 注意没有插入操作,只有添加,删除,查找,修改操作。
type ArrayInterface interface {
    // 添加
    Add(int, interface{})    // 插入元素
    AddLast(interface{})
    AddFirst(interface{})
    // 删除
    Remove(int) interface{}
    RemoveFirst() interface{}
    RemoveLast() interface{}
    // 查找
    Find(interface{}) int // 查找元素返回第一个索引
    FindAll(interface{}) []int // 查找元素返回所有索引
    Contains(interface{}) bool // 查找是否存在元素
    Get(int) interface{}
    // 修改
    Set(int, interface{})
    // 基本方法
    GetCapacity() int // 获得数组容量
    GetSize() int  // 获得元素个数
    IsEmpty() bool // 查看数组是否为空
}

大概有3种设计方案

  1. 普通方案:两倍扩容+挨个元素拷贝。
  2. 仿造slice切片的方案:数组做底层存储+类似窗户的索引+ 更灵活的扩容+数组整体拷贝。
    删除元素时不需要真的删除,只是移动索引。
    增加元素时需要往底层数组添加元素,且移动索引。
  3. 加入COW机制的方案:针对多线程读多写少场景。// 附录2

方案1

// 附录1

方案2: slice的方案

  • 扩容策略:比较复杂
  • 数据迁移策略:整体拷贝

方案3: 针对并发读多写少的优化

  • CopyOnWriteArrayList使用场景
    1、读多写少(白名单,黑名单,系统配置的信息,商品类目的访问和更新场景),为什么?因为写的时候会复制新集合。
    2、集合不大,为什么?因为写的时候会复制新集合。
    实时性要求不高,为什么,因为有可能会读取到旧的集合数据。---- 也就是允许读到旧数据。

方案4

// 附录4
Q:golang的slice动态扩展实现为何不用动态链表?
A:对内存不优化,有gc压力。

扩展

slice并发不安全的问题

参考

  1. Go 语言实现动态数组
    https://github.com/hetong12345/goDataStructure/blob/master/array.go#L101
    https://github.com/JeffreyBool/array/blob/master/array.go
  2. 简单说一说Java中的CopyOnWriteArrayList
  3. 并发容器之CopyOnWriteArrayList
  4. golang的slice动态扩展实现为何不用动态链表?


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM