I believe the solution you hint at in your question is actually what you need, except for a small detail.
You suggested:
I thought I can preallocate memory for maximum number of elements it may have to store and then always put the new element at the end so that no reallocation or move of other elements is necessary.
If indeed you can establish a reasonable maximum number of entries, then I would suggest you pre-allocate an array (e.g. using std::array
if the maximum is known at compile time, or a std::vector
otherwise) with that number of entries, establish an element count (to count the number of elements currently stored), and proceed as follows:
- Initially you set the count to 0
- When you insert an element, you add it to the end and increment the count
- When you delete an element, you swap it with the last element and decrement the count
- For random access (in the sense you described it, i.e. literally picking an element randomly) you determine a random number between 0 and count, and pick that element
The only detail I changed is element deletion, which I suggest you implement as switch positions with the last element.
Possible implementation:
#include <vector>
#include <utility>
#include <iostream>
template <typename Elem>
class randomaccesstable
{
public:
randomaccesstable(std::size_t initial_size)
: data_(initial_size) , count_(0)
{ }
randomaccesstable &push_back(const Elem &elem)
{
if (count_ < data_.size())
data_[count_++] = elem;
else {
data_.push_back(elem);
++count_;
}
return *this;
}
randomaccesstable &remove(const std::size_t index)
{
if (index < count_)
{
std::swap(data_[index],data_[count_-1]);
--count_;
}
return *this;
}
const Elem &operator[](const std::size_t index) const
{ return data_[index]; }
Elem &operator[](const std::size_t index)
{ return data_[index]; }
std::size_t size() const
{ return count_; }
private:
std::vector<Elem> data_;
std::size_t count_;
};
int main()
{
randomaccesstable<int> table(10);
table.push_back(3);
table.push_back(12);
table.push_back(2);
for (std::size_t i = 0 ; i < table.size() ; ++i)
std::cout << table[i] << ' ';
std::cout << '\n';
table.remove(1); // this removes the entry for 12, swapping it for 2
for (std::size_t i = 0 ; i < table.size() ; ++i)
std::cout << table[i] << ' ';
std::cout << '\n';
return 0;
}