我需要更轻的容器,必须存储到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"数据,这意味着每次触摸存储器时都会出现故障。
哪个容器是合适的候选人? 这听起来像一个"索引"队列中。
答案 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
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以获取各种“扁平”索引集合。这将把所有东西都存储在一个向量中而不需要内存操作,但是在顶部添加一个层来使它成为一个集合或映射,可以通过哪些元素进行索引,并且能够分辨哪些元素不存在。