双索引的最佳容器

发布于 2024-07-06 15:25:54 字数 518 浏览 8 评论 0原文

设置允许双索引的容器的最佳方法(在 C++ 中)是什么? 具体来说,我有一个对象列表,每个对象都由一个键索引(每个键可能有多个)。 这意味着多重映射。 然而,这样做的问题在于,这意味着查找对象位置的线性查找可能比线性查找更糟糕。 我宁愿避免重复数据,因此让每个对象维护自己的坐标并必须在地图中移动自己会很糟糕(更不用说移动自己的对象可能会在成员函数中间接调用析构函数!)。 我宁愿有一些容器通过对象指针和坐标维护索引,并且对象本身保证稳定的引用/指针。 然后每个对象都可以将迭代器存储到索引(包括坐标),充分抽象,并知道它在哪里。 Boost.MultiIndex 似乎是最好的主意,但它非常可怕,而且我不希望我的实际对象需要是常量。

你会推荐什么?

编辑:Boost Bimap 看起来不错,但它提供稳定的索引吗? 也就是说,如果我更改坐标,对其他元素的引用必须保持有效。 我想使用指针进行索引的原因是因为对象没有内在排序,并且指针可以在对象更改时保持不变(允许其在 Boost MultiIndex 中使用,IIRC 确实提供了稳定的索引)。

What is the best way (in C++) to set up a container allowing for double-indexing? Specifically, I have a list of objects, each indexed by a key (possibly multiple per key). This implies a multimap. The problem with this, however, is that it means a possibly worse-than-linear lookup to find the location of an object. I'd rather avoid duplication of data, so having each object maintain it's own coordinate and have to move itself in the map would be bad (not to mention that moving your own object may indirectly call your destructor whilst in a member function!). I would rather some container that maintains an index both by object pointer and coordinate, and that the objects themselves guarantee stable references/pointers. Then each object could store an iterator to the index (including the coordinate), sufficiently abstracted, and know where it is. Boost.MultiIndex seems like the best idea, but it's very scary and I don't wany my actual objects to need to be const.

What would you recommend?

EDIT: Boost Bimap seems nice, but does it provide stable indexing? That is, if I change the coordinate, references to other elements must remain valid. The reason I want to use pointers for indexing is because objects have otherwise no intrinsic ordering, and a pointer can remain constant while the object changes (allowing its use in a Boost MultiIndex, which, IIRC, does provide stable indexing).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧时光的容颜 2024-07-13 15:25:55

我根据您的文章做出几个假设:

  • 复制和比较密钥的成本很低
  • 系统中应该只有该对象的一份副本
  • 相​​同的密钥可能引用许多对象,但只有一个对象对应于给定的密钥(一个-to-many)
  • 您希望能够有效地查找哪些对象对应于给定的键,以及哪个键对应于给定的对象

我建议:

  • 使用链接列表或其他容器来维护所有对象的全局列表系统中的对象。 对象分配在链表上。
  • 创建一个 std::multimap 将键映射到对象指针,指向链表中的单个规范位置。
  • 执行以下操作之一:
    • 创建一个 std::map,允许查找附加到特定对象的键。 确保您的代码在密钥更改时更新此映射。 (如果您需要多对多关系,这也可以是 std::multimap。)
    • 向包含当前KeyObject 添加一个成员变量(允许 O(1) 查找)。 确保您的代码在密钥更改时更新此变量。

由于您的文章提到“坐标”作为键,您可能还有兴趣阅读 查找 3D 坐标是否已使用的最快方法

I'm making several assumptions based on your writeup:

  • Keys are cheap to copy and compare
  • There should be only one copy of the object in the system
  • The same key may refer to many objects, but only one object corresponds to a given key (one-to-many)
  • You want to be able to efficiently look up which objects correspond to a given key, and which key corresponds to a given object

I'd suggest:

  • Use a linked list or some other container to maintain a global list of all objects in the system. The objects are allocated on the linked list.
  • Create one std::multimap<Key, Object *> that maps keys to object pointers, pointing to the single canonical location in the linked list.
  • Do one of:
    • Create one std::map<Object *, Key> that allows looking up the key attached to a particular object. Make sure your code updates this map when the key is changed. (This could also be a std::multimap if you need a many-to-many relationship.)
    • Add a member variable to the Object that contains the current Key (allowing O(1) lookups). Make sure your code updates this variable when the key is changed.

Since your writeup mentioned "coordinates" as the keys, you might also be interested in reading the suggestions at Fastest way to find if a 3D coordinate is already used.

小草泠泠 2024-07-13 15:25:55

很难理解你到底在用它做什么,但它看起来像 boost bimap 就是你想要的。 除了特定的用例之外,它基本上是 boost 多索引,并且更易于使用。 它允许基于第一个元素或第二个元素进行快速查找。 为什么要通过地址查找地图中对象的位置? 使用抽象并让它为您完成所有工作。 请注意:映射中所有元素的迭代时间复杂度为 O(N),因此可以保证查找您正在考虑的方式为 O(N) (不是更糟)。

Its difficult to understand what exactly you are doing with it, but it seems like boost bimap is what you want. It's basically boost multi-index except a specific use case, and easier to use. It allows fast lookup based on the first element or the second element. Why are you looking up the location of an object in a map by its address? Use the abstraction and let it do all the work for you. Just a note: iteration over all elements in a map is O(N) so it would be guaranteed O(N) (not worse) to look up the way you are thinking of doing it.

荆棘i 2024-07-13 15:25:55

一种选择是使用两个引用shared_ptr 的std::map。 像这样的事情可能会让你继续:

template<typename T, typename K1, typename K2>
class MyBiMap
{
public:
    typedef boost::shared_ptr<T> ptr_type;

    void insert(const ptr_type& value, const K1& key1, const K2& key2)
    {
        _map1.insert(std::make_pair(key1, value));
        _map2.insert(std::make_pair(key2, value));
    }

    ptr_type find1(const K1& key)
    {
        std::map<K1, ptr_type >::const_iterator itr = _map1.find(key);
        if (itr == _map1.end())
            throw std::exception("Unable to find key");
        return itr->second;
    }

    ptr_type find2(const K2& key)
    {
        std::map<K2, ptr_type >::const_iterator itr = _map2.find(key);
        if (itr == _map2.end())
            throw std::exception("Unable to find key");
        return itr->second;
    }

private:
    std::map<K1, ptr_type > _map1;
    std::map<K2, ptr_type > _map2;
};

编辑:我刚刚注意到多重地图的要求,这仍然表达了这个想法,所以我会留下它。

One option would be to use two std::maps that referenced shared_ptrs. Something like this may get you going:

template<typename T, typename K1, typename K2>
class MyBiMap
{
public:
    typedef boost::shared_ptr<T> ptr_type;

    void insert(const ptr_type& value, const K1& key1, const K2& key2)
    {
        _map1.insert(std::make_pair(key1, value));
        _map2.insert(std::make_pair(key2, value));
    }

    ptr_type find1(const K1& key)
    {
        std::map<K1, ptr_type >::const_iterator itr = _map1.find(key);
        if (itr == _map1.end())
            throw std::exception("Unable to find key");
        return itr->second;
    }

    ptr_type find2(const K2& key)
    {
        std::map<K2, ptr_type >::const_iterator itr = _map2.find(key);
        if (itr == _map2.end())
            throw std::exception("Unable to find key");
        return itr->second;
    }

private:
    std::map<K1, ptr_type > _map1;
    std::map<K2, ptr_type > _map2;
};

Edit: I just noticed the multimap requirement, this still expresses the idea so I'll leave it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文