I am currently working in a project (I have simplified it for the sake of question) that requires a container to store students and retrieve them as fast as possible or access them with \$O(1)\$ if possible. I will also be deleting students from any place but always adding them at the end of the queue.
I have three struct
s: Student
, Class
and University
.
Student
: has name and ageClass
: has name of course and array of shared pointers of size 2University
: has deque of Students and deque of class
#include <memory>
#include <deque>
#include <string>
#include <iostream>
constexpr size_t SIZE = 150;
struct Student{
std::string name;
size_t age;
Student(std::string n, size_t a){
name = n;
age = a;
}
~Student(){
std::cout << __func__ << std::endl;
}
};
struct Class{
// Lets assume that each class has 2 student for the sake of argument
// and exactly 2.
std::shared_ptr<Student> my_class[2];
std::string course;
Class(std::string c, std::shared_ptr<Student> n0, std::shared_ptr<Student> n1){
course = c;
my_class[0] = n0;
my_class[1] = n1;
}
};
struct University{
std::deque<std::shared_ptr<Student>> student_deq;
std::deque<std::shared_ptr<Class>> class_deq;
void add_student(std::string n, size_t a){
student_deq.push_back(std::make_shared<Student>(n, a));
}
void add_class(std::string c, size_t i, size_t j){
class_deq.push_back(std::make_shared<Class>(c, student_deq[i], student_deq[j]));
}
};
int main(){
University u;
for(size_t i = 0; i < SIZE; i++){
u.add_student("Xyz", i);
}
for(size_t j = 0; j < SIZE; j++){
if(j+1 == SIZE)
u.add_class("abc", j, 0);
else u.add_class("abc", j, j+1);
}
std::cout << u.student_deq[9]->name << std::endl;
return 0;
}
I have some questions:
Do you think my choice of deque as a data structure (container) is appropriate?
I want the retrieve (from anywhere) / add (to end)/ remove (from anywhere) to be \$O(1)\$. I use deque because unlike vector it does not have to copy all the data from beginning of the array in case the memory grows and have to allocate new slab of contiguous memory. Since deque is made of small chunks of contiguous memory not all chunks need to be copied (at least that is what I think of). Also the retrieve and add to end is \$O(1)\$ in case of deque. I know remove is \$O(n)\$: \$n\$ being size of one of the chunk of contiguous memory the item reside in.
I think I could also use
HashMap
(unordere_map
) but I read somewhere thathash_map
does not necessarily give \$O(1)\$ because of collision when data gets huge and also my student number will definitely grow hugely (e.g. 100,000 and up also for sake of argument). I also heard that implementing hash function to avoid collision is complex.I also think I can't use
std::map
since retrieval is \$O(ln)\$.Do you think the use of
shread_ptr
inside the deque for memory management is a good design choice?I used
sharad_ptr
so that I don't have to do the manual delete of the allocated memory. I also make the class store theshared_ptr
rather than a copy of the student because theStudent
class may contain other information that might be costly to copy. Also a student can be in more then one class ( This way instead of creating multiple copyshared_ptr
will only increase the count - Here also I am not sure is it really necessary to increase count because since there will only be one copy I was also thinking about passing by const ref so that we dont have the overhead of increasing the count itself)Is there a container which gives \$O(1)\$ for retrieval, adding to the end and removal from anywhere in the container (of-course except hash map as I discussed about it above) or anything that is close enough that provide these features?