我应该使用哪个容器进行随机访问,便宜的添加和删除(没有de / allocation),具有已知的最大大小?

时间:2016-07-06 10:04:10

标签: c++ stl containers

我需要更轻的容器,必须存储到128 unsigned int。 它必须添加,编辑和删除快速访问它的每个元素,而不是每次都分配新的内存(我已经知道它将是最大128)。

如:

add int 40 at index 4 (1/128 item used)
add int 36 at index 90 (2/128 item used)
edit to value 42 the element at index 4
add int 36 at index 54 (3/128 item used)
remove element with index 90 (2/128 item used)
remove element with index 4 (1/128 item used)

......等等。因此,每次我只能迭代添加到容器中的实际数量的元素,而不是全部,并检查是否为NULL。

在此过程中,正如我所说,它不能分配/重新分配新内存,因为我使用的是管理" audio"数据,这意味着每次触摸存储器时都会出现故障。

哪个容器是合适的候选人? 这听起来像一个"索引"队列中。

9 个答案:

答案 0 :(得分:9)

据我所知,你有两个操作

在单元格索引中插入/替换元素 删除单元格索引

的元素

和一个谓词

目前是否已占用单元格索引

这是一个数组和一个位图。插入/替换时,将值粘贴在数组单元格中并设置位图位。删除时,清除位图位。当你问,你查询位图位。

答案 1 :(得分:7)

您可以使用std::vector<int>并执行vector.reserve(128);以防止向量分配内存。但这并不能让你跟踪特定的指数。

如果您需要来跟踪&#39;索引&#39;你可以使用std::vector<std::pair<int, int>>。但这并不允许随机访问。

答案 2 :(得分:6)

如果您只需要便宜的设置和擦除值,只需使用数组即可。您 可以通过在另一个数组(或位图)中标记它们来跟踪所使用的单元格。或者只是将一个值(例如0或-1)定义为“未使用”值。

当然,如果需要迭代所有使用过的单元格,则需要扫描整个数组。但这需要做出权衡:要么在添加和删除期间做更多工作,要么在搜索过程中做更多工作。 (请注意,.insert()中间的vector<>会移动数据。)

在任何情况下,128个元素都是如此之少,以至于扫描整个数组的工作可以忽略不计。坦率地说,我认为比vector更复杂的东西将是完全矫枉过正的。 :)

大致是:

unsigned data[128] = {0}; // initialize
unsigned used[128] = {0};
data[index] = newvalue; used[index] = 1; // set value
data[index] = used[index] = 0;           // unset value
// check if a cell is used and do something 
if (used[index]) { do something } else { do something else } 

答案 3 :(得分:5)

我建议使用一系列向量,一个用于保存活动索引,另一个用于保存数据:

class Container
{
  std::vector<size_t> indices;
  std::vector<int> data;

  size_t index_worldToData(size_t worldIndex) const
  {
    auto it = std::lower_bound(begin(indices), end(indices), worldIndex);
    return it - begin(indices);
  }

public:
  Container()
  {
    indices.reserve(128);
    data.reserve(128);
  }

  int& operator[] (size_t worldIndex)
  {
    return data[index_worldToData(worldIndex)];
  }

  void addElement(size_t worldIndex, int element)
  {
    auto dataIndex = index_worldToData(worldIndex);
    indices.insert(it, worldIndex);
    data.insert(begin(data) + dataIndex, element);
  }

  void removeElement(size_t worldIndex)
  {
    auto dataIndex = index_worldToData(worldIndex);
    indices.erase(begin(indices) + dataIndex);
    data.erase(begin(indices) + dataIndex);
  }

  class iterator
  {
    Container *cnt;
    size_t dataIndex;

  public:
    int& operator* () const { return cnt.data[dataIndex]; }
    iterator& operator++ () { ++dataIndex; }
  };

  iterator begin() { return iterator{ this, 0 }; }
  iterator end() { return iterator{ this, indices.size() }; }
};

(免责声明:编译器未触及代码,省略前置条件检查)

这个具有对数时间元素访问,线性时间插入和删除,并允许迭代非空元素。

答案 4 :(得分:4)

您可以使用双向链表和一组节点指针。 预分配128个列表节点并将其保留在freelist上。 创建一个空的itemlist。 分配一个名为items

的128个节点指针数组
  • 要在i处插入:从freelist弹出头节点,将其添加到 itemlist,设置items[i]指向它。
  • 要访问/更改值,请使用items[i]->value
  • 要在i处删除,请删除items[i]指向的节点,然后将其重新插入“freelist&#39;
  • 要进行迭代,只需走itemlist

除了迭代之外,一切都是O(1),即O(N active_items )。唯一需要注意的是迭代不是索引顺序。

Freelist可以是单链接的,甚至是一系列节点,因为你需要的就是弹出和推送。

答案 5 :(得分:1)

class Container {
  private:
    set<size_t> indices;
    unsigned int buffer[128];
  public:
    void set_elem(const size_t index, const unsigned int element) {
      buffer[index] = element;
      indices.insert(index);
    }
    // and so on -- iterate over the indices if necessary
};

答案 6 :(得分:1)

您可以使用多种方法,我会按照费用的顺序引用它们。

最实惠的解决方案是使用Boost non-standard containers,特别感兴趣的是flat_map。基本上,flat_map在动态数组提供的存储上提供map的接口。

您可以在开始时调用其reserve成员,以避免以后分配内存。

稍微复杂一点的解决方案是编写自己的内存分配器。

allocator的接口相对容易处理,因此编码分配器非常简单。创建一个永远不会释放任何元素的池分配器,将其加热(分配128个元素)并准备好了:它可以插入任何集合中以使其无需内存分配。

特别感兴趣的是,这里当然是std::map

最后,有自己动手的道路。更明显的是:标准容器支持的操作数量只是...... 巨大

尽管如此,如果你有时间或者只能接受这些操作的一部分,那么这条道路有一个不可否认的优势:你可以根据自己的需要定制容器。

这里特别感兴趣的是拥有一个std::vector<boost::optional<int>>个128个元素的想法...除了因为这个表示空间效率很低,我们使用面向数据的设计来代替两个向量:{{ 1}}和std::vector<int>,它更紧凑,甚至......

std::vector<bool>

这是紧凑的免费分配。

现在,迭代需要迭代当前元素的bitset,这可能看起来很浪费,但是说bitset只有16个字节,所以它是轻而易举的! (因为在这样的规模下,记忆局部性胜过大O复杂度)

答案 7 :(得分:0)

为什么不使用std::map<int, int>,它提供随机访问并且稀疏。

答案 8 :(得分:-1)

如果vector(预先保留)不够方便,请查看Boost.Container以获取各种“扁平”索引集合。这将把所有东西都存储在一个向量中而不需要内存操作,但是在顶部添加一个层来使它成为一个集合或映射,可以通过哪些元素进行索引,并且能够分辨哪些元素不存在。