【问题标题】:Fast way of getting a specific struct object from an std::vector从 std::vector 获取特定结构对象的快速方法
【发布时间】:2013-09-02 03:47:11
【问题描述】:

我有一个简单的struct,叫做Item

struct Item {
    unsigned int id;
    std::string name;

    Item() : id( 0 ), name( std::string( "" ) ) {

    };
};

然后我有这个类来保存所有这些Items。

class ItemData {
public:
    std::vector< Item > m_Items;
private:
    void load() {
         // Parse a JSON string to fill up m_Items vector with
         // Item objects.
    }

    const Item getItem( unsigned int pID ) {
        // Create an "empty" Item object with ID = 0 and name = ""
        Item temp = Item();

        // Loop through the vector
        for ( unsigned int i = 0; i < m_Items.size(); i++ ) {
            // Check if the current Item object has the id we are looking for
            if ( m_Items.at( i ).id == pID ) {
                // The item is inside the vector, replace temp with the
                // target vector
                temp = m_Items.at( i );

                // Stop looping
                break;
            }
        }

        // If pID was found, temp will have the values of the object inside the vector
        // If not, temp will have id = 0 and name = ""
        return temp;
    }
};

我觉得这个方法太费时间了,特别是如果ItemData::getItem(unsigned int) 在循环中被调用。

有没有一种更有效的方法可以在不循环遍历向量的情况下将对象放入向量中?我应该改用其他容器吗(例如std::list)?

【问题讨论】:

  • 您的 ID 有哪些示例?对于不同的容器,std::map 的查找时间为 O(log n)。
  • @DarkFalcon ID 只是无符号整数。 011762000 是一些有效的 ID。
  • name( std::string( "" ) ) 完全是多余的。
  • getItem 很恶心。一方面,你正在重新发明轮子。 std::find 为您执行此操作,前提是您提供了一个仿函数(或 C++11 中的 lambda)。另一方面,getItem 不只是获取项目。它找到它,然后替换它。简单胜于复杂。
  • @JohnDibling:“它取代了它”是什么意思?

标签: c++ c++11 vector struct


【解决方案1】:

改用std::map

class ItemData {
public:
    std::map<unsigned, Item> m_Items;
private:
    void load() {
         // Parse a JSON string to fill up m_Items vector with
         // Item objects.
    }

    const Item getItem(unsigned id) const {
        std::map<unsigned, Item>::const_iterator it = m_Items.find(id);
        if (it != m_Items.end())
            return it->second;
        return Item();
    }
};

你也可以考虑std::unordered_map

【讨论】:

  • 我对@9​​87654324@ 有点困惑。我可以说unsigned 将是每个Item 对象的键,但unsigned 不应该是unsigned int 吗?还是它们属于同一类型?
  • @LanceGray 是的,它们是一样的。
  • 这是写unsigned int 的好理由。好的,您节省了四次击键:干得好!但是,您因此扼杀了清晰度。
【解决方案2】:

如果您只想遍历容器中的所有项目,那么 vector 很棒。如果您在线性搜索与性能无关的情况下相对不频繁地进行查找,那么向量可能仍然可以。

如果您需要能够通过其 id 查找项目并且不关心保留项目在容器中的插入顺序,那么根据您的排序需要使用 mapunordered_map,容器大小等

如果您需要维护插入顺序并且通过 id 进行快速查找并且您不会从向量中删除项目,那么我建议您使用 @987654323 @of id 来索引,并在添加新项目时维护 id-index 映射。

【讨论】:

  • 我想保留插入顺序以及快速查找,但可能不需要删除项目。
  • @LanceGray:我根据您在我的回答中的评论提出了一个解决方案。
【解决方案3】:

绝对不是std::list。我相信您正在寻找std::map(它将唯一ID 映射到对象)。或者,std::set(仅存储唯一对象)与自定义比较器,以便 Items 将根据它们的 id 进行比较。

set 的缺点是将对象存储为const。我相信map 最适合您(将id 存储一次作为映射键并存储在Item 中的开销很低)。

【讨论】:

  • 我不介意我的对象是const,因为一旦设置了容器及其对象,我很可能不会更改它们。
【解决方案4】:

我想保留插入顺序以及快速查找,但可能不需要删除项目

所以你想要的是为向量创建一个索引。那就是创建一个哈希表,将项目 id 映射到向量中的项目位置:

class ItemData {
    vector< Item > m_Items;
    unordered_map<unsigned int, size_t> m_ItemsIndex;

    void prepare_index()
    {
        for (size_t i = 0; i < m_Items.size(); i++)
           m_ItemsIndex[m_Items[i].id] = i;
    }

    Item& get_item(unsigned int id)
    {
        size_t pos = m_ItemsIndex[id];
        return m_Items[pos];
    }
}

这将查找速度从线性 (O(n)) 提高到恒定时间 (O(1))。

load 末尾致电prepare_index。您还想添加错误检查等,但您明白了。

保留插入顺序,因为您仍然可以直接迭代向量。

【讨论】:

    猜你喜欢
    • 2012-03-15
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 2017-02-06
    • 1970-01-01
    • 2019-03-24
    • 2011-03-04
    相关资源
    最近更新 更多