问题
I've been trying out to figure an effective way to store and retrieve a number of objects. Let me explain what I'm trying to achieve, then list the options I've come up with (But am unhappy with).
The following technically does what I need it to do, but is an obvious no-no:
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::unordered_map<uint32_t, Component*>>>>
//Scene -> Layer -> Type -> Id -> Component*
The most inner map holds the Components based on their ID. The one before it has a map per type (Subclasses of Component). The latter is done so that when I retrieve them, I can dynamically cast them to their type with full safety knowing the TYPE hash map only contains pointers of their type, also allowing the use of count for quickly checking if something exists at a certain ID. The map following that stores them by layer, the first map stores them by scene. At any point, about 30-50 scenes will be held, which each contain about 6-10 layers which each contain about 30-40 types, which per type contains anywhere from 1 to 500 objects.
Each cycle we'll be iterating over the pointers based on their type, one layer at a time. Scenes change rarely (Every 2-3 minutes). Components are accessed with a combination of Type and Id. Code routinely checks which other Component types are present at the same Id. Scenes, layers and types are access through their name, which is stored as a 32 bits CRC hash. Speed is crucial. IDs are numbers assigned by the code, simply going from 0 and up. IDs are unique within each scene.
No doubt there's some crazy (read: common) idiom that helps me out and I have never heard about. Any one know one? So far none of the alternatives I've come up with are acceptable, but I'll list them regardless:
Option 1:
std::unordered_map<uint32_t, std::vector<Component*>>
ID -> Component*
Component holds which type, scene and layer it is from, whenever we iterate over all entries we ignore the ones not from the current scene or layer. Alternatively, store them in order so that you only have to iterate over a certain range. The vector holds the Components and when we need to access a component of a certain type we search through the vector. Not ideal as it would require many searches a cycle. Alternatively use an unordered_map in place of the vector.
Option 2:
The same as the nested maps, but with vectors. A map converts Id to index inside the vector.
Option 3:
std::vector<Component*>
std::unordered_map<uint32_t, std::vector<int>>
(Type / Layer / Scene / Id) -> Component* Store all components simply with the index of the vector. Have an unordered_map which contains vectors of indexes in the main storage vector. Both IDs and string hashes can exist as we check for collision between the two (Unlikely). Names need to be unique for scenes, layers and types. IDs return a vector of all the indexes for components part of that ID, Name or types return vectors containing all indexes of that type or scene. Feels hackish, all those iterations of those vectors.
Option 4:
Components gets a 'Component* next' pointer to iterate through components that belong to the same entity. Last component links to the first. Components once again get type and scene / layer members.
回答1:
Specify you own key, with hashing function and equal function.
class cKey
{
public:
size_t scene;
size_t layer;
size_t type;
size_t id;
};
unordered_map< cKey, Component*,
hashkey, equalkey >
How would one iterate over all components of say, one layer?
cKey key;
key.scene = S;
key.layer = L;
for( key.type = 0; key.type< LastType; key.type ++ ) {
for( key.id = 0; key.id < LastID; key.id++ ) {
Component * pC = the_map.find( key ).second;
...
You can find an implementation at https://gist.github.com/JamesBremner/d71b158b32e4dd8ffaf8cbe93cf3f180 which iterates over a layer in a map of 50,000 components in 250msecs.
回答2:
I would suggest speparating the maps into multiple maps:
std::unordered_map<std::uint32_t, std::vector<std::uint32_t>> layer_by_scene;
std::unordered_map<std::uint32_t, std::vector<std::uint32_t>> entity_by_layer;
std::unordered_map<uint32_t, std::vector<std::uint32_t>> component_by_entity;
std::unordered_map<uint32_t, Component*> components;
However, note that in a usual Entity-Component-System, you would try to avoid pointer chasing and jumping around in node based container.
来源:https://stackoverflow.com/questions/56687921/alternative-for-quad-nested-unordered-map-monstrosity