【问题标题】:Data structure that maps unique id to an object将唯一 ID 映射到对象的数据结构
【发布时间】:2010-02-10 11:44:19
【问题描述】:

我正在寻找一种 C++ 数据结构,它可以让我将对象与唯一的数值(键)相关联,并在从容器中删除相应对象后重新使用这些键。所以它基本上有点像混合地图/队列结构。我目前的实现是一个

std::map<size_t, TObject>

然后我插入这样的对象:

 size_t key = (m_Container.end()--)->first + 1;
 m_Container[key] = some_object;

这对我的目的来说很好(我永远不会分配超过 size_t 个对象);但我仍然想知道是否有更专业的容器可用,最好已经在 stl 或 boost 中,或者有办法使用另一个容器来实现这个目标。

(当然我可以,而不是在我的地图中获取最高的键并添加一个,每次都通过地图并搜索第一个可用的键,但这会将复杂度从 O(1) 降低到 O(n)如果 API 很简单,那就太好了

size_t new_key = m_Container.insert(object);

)。

有什么想法吗?

【问题讨论】:

  • 容器为空时会中断 (end==begin)。另外,您可能错过了m_Container.back()
  • 在我的代码中,我确实检查了地图是否为空,这只是为了说明。此外,std::map 没有 .back()。
  • m_Container.rbegin()->first 可能比 (m_Container.end()--)->first ... 更具可读性?
  • 是的。尽管如此,随着情人节即将到来,我认为 ()--)-> 会很好地融入花卉主题;)
  • 注意:后递减运算符 (--) 递减左值表达式。 BUT 表达式的结果(返回给表达式进行进一步处理)是原始值。因此,用于首先获取的运算符 -> 被用于 end() (无效操作)。

标签: c++ data-structures stl


【解决方案1】:

如果您永远不会分配超过 size_t 的键,那么我建议您只使用静态计数器:

size_t assign_id()
{
    static size_t next_id;
    return next_id++;
}

如果你想要一个不错的 API:

template<class Container>
size_t insert(Container & container, TObject const & obj)
{
     container.insert(obj);
     return assign_id();
}

std::set<TObject> m_Container;
size_t new_key = insert(m_Container, object);

【讨论】:

  • objectnew_key 是如何相互关联的?容器不应该是map 而不是set
  • 我认为 OP 会自己解决这个问题(可能通过将密钥存储在对象的数据成员中)。
  • 此外,OP 似乎使用地图只是为了保持迄今为止创建的许多对象。这就是为什么我提出我的函数作为实现相同效果的更轻量级的方式。
  • 我需要一种以不显眼的方式跟踪对象的方法,即不更改被跟踪的对象(因此不添加 ID)。即使我分配了 ID,我仍然需要在制作新对象时找出最新的 ID,只是转移问题。我需要跟踪对象以了解哪些仍在等待处理的队列中,哪些已完成。
  • 我的代码解决了知道最新ID是什么的问题。如果您还需要为每个对象附加一个 ID,那么我建议您将我的代码与 map&lt;TObject, size_t&gt; 结合使用
【解决方案2】:

我不确定你到底想从你的 ID 中得到什么。碰巧的是,每个对象已经拥有一个唯一的 ID:它的地址!没有两个不同的对象具有相同的地址,并且对象的地址在其生命周期内不会改变。

std::set&lt;T&gt; 通常将其 T 值存储为较大节点的成员,而不是独立对象。尽管如此,T 子对象永远不会移动,因此它们的地址也是稳定的唯一标识符。

【讨论】:

  • 我需要“按值”识别对象,将它们的副本传递给单独的线程,根据该对象的内容执行操作,然后将消息发送回 UI说“对象 x 已处理”,而不发回实际对象(与通知无关)。我需要能够从仍在队列中的对象列表中删除已处理的对象。
【解决方案3】:

创建已删除密钥的std::set&lt;key_type&gt; removed_keys;。如果removed_keys 不为空,则使用来自removed_keys 的密钥,否则创建一个新密钥。

【讨论】:

  • 或者可能不是一组。重新使用释放的密钥的顺序似乎并不重要,在这种情况下,堆栈会更合适(并且可能更有效)。
【解决方案4】:

为什么不直接使用向量?

std::vector<TObject> m_Container;

size_t key = m_Container.size();
m_Container.push_back(some_object);

当然,如果您有其他使用特性,这可能完全没用。但是由于您只描述了插入和对密钥的需要(因此提取),因此很难给出任何其他明确的答案。但是根据这两个要求, std::vector 应该可以正常工作。

如果您有其他一些使用特征,例如:(可以删除元素)、(我们在大块中插入元素)、(我们不经常插入元素)等,这些将是有趣的事实,可能会改变人们给出的建议。

您提到要搜索未使用的元素 ID。这表明您可能正在删除元素,但我没有看到任何明确的要求或使用删除元素的地方。

看看你上面的代码:

size_t key = (m_Container.end()--)->first + 1;

这并没有做你认为它正在做的事情。
也是等价的:

size_t key = m_Container.end()->first + 1;
m_Container.end()--;

post 减量运算符修改左值。但是表达式的结果是原始值。因此,您将运算符 -> 应用于 end() 返回的值。这是(可能)未定义的行为。

查看标准:

章节:5.2.6 递增和递减
后缀 ++ 表达式的值是其操作数的值。

m_Container.end()--  // This sub-expresiion returns m_Container.end()

替代方案:

#include <vector>

template<typename T>
class X
{
    public:
        T const& operator[](std::size_t index) const    {return const_cast<X&>(*this)[index];}
        T&       operator[](std::size_t index)          {return data[index];}
        void        remove(std::size_t index)           {unused.push_back(index);}

        std::size_t insert(T const& value);
    private:
        std::vector<T>                  data;
        std::vector<std::size_t>        unused;
};

template<typename T>
std::size_t X<T>::insert(T const& value)
{
    if (unused.empty())
    {
        data.push_back(value);
        return data.size() - 1;
    }
    std::size_t result  = unused.back();
    unused.pop_back();
    data[result]    = value;
    return result;
}

【讨论】:

  • 更正后增量,这是一个愚蠢的错误。使用矢量当然可以做到这一点,它可以使用任何类型的容器来完成。我想我的问题用错了。我不是在寻找“解决方案”,我已经有了一个并且可以想到其他 10 个,我更加理论化,想知道这是否是一种已知模式,是否存在问题的最佳复杂性。
【解决方案5】:

您是否有理由需要 std::map 不删除 键、值 对?

这听起来像是过早优化的尝试。

一种方法是将 value 部分替换为虚拟或占位符值。从长远来看,问题在于只要密钥存在,就可以从std::map 中提取虚拟值。每次访问 std::map 时,您都需要添加一个虚拟值检查。

因为您想维护一个没有值的键,所以您很可能必须编写自己的容器。当客户端访问没有值的键时,您将特别需要设计代码来处理这种情况。

使用标准容器和没有值对的键似乎没有性能提升。但是,就内存而言,可能会有所收获。您的问题会减少动态内存的碎片;因此不必为同一个键重新分配内存。您必须做出权衡。

【讨论】:

  • 不,我不想保留键但删除对象,我想删除键/值对。
猜你喜欢
  • 2021-02-11
  • 2015-06-27
  • 1970-01-01
  • 1970-01-01
  • 2016-12-27
  • 2013-10-08
  • 1970-01-01
  • 2020-08-01
  • 2011-04-24
相关资源
最近更新 更多