使用不同的关键字搜索数据

Searching data using different keys

本文关键字:关键字 搜索 数据      更新时间:2023-10-16

我不是C++和STL方面的专家。

我使用Map中的结构作为数据。关键是某个类别C1。我想访问相同的数据,但也使用不同的密钥C2(其中C1和C2是两个不相关的类)。

这在不复制数据的情况下可能吗?我试着在谷歌上搜索,但很难找到一个我能理解的答案。

这适用于不支持boost库的嵌入式目标。

有人能提供帮助吗?

您可以将指向Data指针存储为std::map值,并且您可以有两个具有指向相同数据的不同键的映射。

我认为在共享数据所有权的情况下,智能指针(如std::shared_ptr)是一个不错的选择:

#include <map>       // for std::map
#include <memory>    // for std::shared_ptr
....
std::map<C1, std::shared_ptr<Data>> map1;
std::map<C2, std::shared_ptr<Data>> map2;

可以使用std::make_shared()来分配Data的实例。

不在标准库中,但Boost提供boost::multi_index

两个不同类型的键

我必须承认我看错了一点,并没有真正注意到你想要两个不同类型的键,而不是值。不过,解决方案将基于以下内容。其他答案几乎都是需要的,我只想补充一点,你可以制作一个通用的查找函数:(C++14 ish伪代码)。

template<class Key> 
auto lookup (Key const& key) { }

并将其专门用于您的密钥(可以说比SFINAE更容易)

template<>
auto lookup<KeyA> (KeyA const& key) { return map_of_keys_a[key]; }

CCD_ 7也是如此。

如果您想将其封装在一个类中,一个明显的选择是将lookup更改为operator[]


类型相同但值不同的键

想法1

我能在60秒内想到的最简单的解决方案:(最简单的意思是应该认真思考)。我也会默认切换到unordered_map

map<Key, Data> data;
map<Key2, Key> keys;

通过data[keys["multikey"]]访问。

这显然会浪费一些空间(复制Key类型的对象),但我假设它们比Data类型小得多。

想法2

另一种解决方案是使用指针;那么复制的唯一成本是一个(智能)指针:

map<Key, shared_ptr<Data>> data;

只要至少有一个键指向Data的对象,它就会是活动的。

在这种情况下,我通常使用非拥有的指针。我将数据存储在一个向量中:

std::vector<Data> myData;

然后我将指针映射到每个元素。不过,由于指针可能会因为矢量的未来增长而无效,因此在这种情况下,我将选择使用矢量索引。

std::map<Key1, int> myMap1;
std::map<Key2, int> myMap2;

不要向客户端公开数据容器。将元素的插入和移除封装在特定的函数中,这些函数到处插入,到处移除。

Bartek的"创意1"很好(尽管没有令人信服的理由更喜欢unordered_map而不是map)。

或者,您可以使用std::map<C2, Data*>std::map<C2, std::map<C1, Data>::iterator>来允许在一次C2关键字搜索后直接访问Data对象,但您需要更加小心,不要访问无效(已擦除)的Data(或者更准确地说,从任何其他用户的角度来看,从两个容器原子地访问erase)。

一个或两个map也可以移动到shared_ptr<Data>,如果这对所有权有帮助的话,另一个可以使用weak_ptr<>。(这些都在C++11标准中,否则显而易见的源代码boost显然不适合你,但也许你已经实现了自己的库或选择了另一个库?这是现代C++的基本类)。

EDIT-哈希表与平衡二进制树

这与这个问题并不特别相关,但已经收到了下面的评论/兴趣,我需要更多的空间来正确地解决它。一些要点:

1) Bartek随意建议从map更改为unordered_map,而不建议影响研究重新迭代器/指针失效,这是危险的,而且没有理由认为这是必要的(这个问题没有提到性能),也没有建议对其进行评测。

3) 程序中相对较少的数据结构对性能关键行为很重要,而且在很多时候,一个与另一个的相对性能无关紧要。支持这一说法——大量代码是用std::map编写的,以确保在C++11之前的可移植性,并且执行得很好。

4) 当性能是一个严重的问题时,建议应该是"Care=>profile",但要说经验法则是可以的——符合"不要过早地悲观"(例如,请参阅Sutter和Alexandrescu的C++编码标准)——如果这里有人问我,我很乐意默认推荐unordered_map——但这不是特别可靠。这与推荐我看到的每一个std::map的用法都有所不同。

5) 这个容器性能方面的跟踪已经开始引入一些有用的见解,但还远远不够全面或平衡。这个问题不是进行这种讨论的合理场所。如果有另一个问题可以解决这个问题,继续讨论是有意义的,并且有人要求我参与,我会在未来一两个月的某个时候这样做。

您可以考虑使用一个普通的std::list来保存所有数据,然后使用各种std::map对象将任意键值映射到指向列表的迭代器:

std::list<Data> values;
std::map<C1, std::list<Data>::iterator> byC1;
std::map<C2, std::list<Data>::iterator> byC2;

也就是说,使用普通迭代器,而不是摆弄或多或少的原始指针。并且std::list中的迭代器具有非常好的无效保证。

我也遇到了同样的问题,起初为共享指针持有两个映射听起来很酷。但你仍然需要管理这两张地图(插入、删除等)

然后我想出了其他的方法。我的理由是;访问具有x-y或半径角的数据。假设每个点都有数据,但点可以描述为笛卡尔x、y或半径角。

所以我写了一个类似的结构

struct MyPoint
{
    std::pair<int, int> cartesianPoint;
    std::pair<int, int> radianPoint;
    bool operator== (const MyPoint& rhs)
    {
         if (cartesianPoint == rhs.cartesianPoint || radianPoint == rhs.radianPoint)
             return true;
         return false;
    }
}

在那之后,我可以用它作为密钥,

std::unordered_map<MyPoint, DataType> myMultIndexMap;  

我不确定你的情况是否相同或可调整到这种场景,但它可以是一种选择。