为同一对象使用Map和List

时间:2015-02-07 22:59:43

标签: c++ list c++11 iterator unordered-map

我正在尝试使用list和unordered_map来存储同一组对象。我是C ++的新手,所以仍然对迭代器感到满意。

说我有以下测试代码:

class Test {
public:
    int x;
    int y;
    int z;
    Test (int, int, int);
}

Test t1 = Test(1,2,3);
Test t2 = Test(2,4,6);
Test t3 = Test(3,6,9);

std::list<Test> list;
std::unordered_map<int, Test> map;

list.push_back(t3);
list.push_back(t2);
list.push_back(t1);
map[101] = t1;
map[102] = t2;
map[103] = t3;

是否可以按键查找对象,然后从对象的引用(或从unordered_map生成器?)生成列表迭代器?

所以,如果我有钥匙102,我可以在恒定时间内查看t2。然后我想要相对于t2在列表中的位置迭代前进/后退/插入/删除。

我可以使用find来获取指向t2的unordered_map迭代器。我不知道如何生成一个从t2开始的列表迭代器(我只能在列表的开头或结尾生成迭代器,并迭代。)

非常感谢任何人指点我对STL和迭代器的好教程。

谢谢!

有感: 这是一种可接受的方法吗?我有很多对象,需要通过整数键有效地查找它们。我还需要保留它们的顺序(与这些整数键无关)并有效地插入/删除/遍历。

2 个答案:

答案 0 :(得分:4)

如果您想要做的是:

  

是否可以按键查找对象,然后从对象的引用(或从unordered_map生成器?)生成列表迭代器?

然后你可以利用list迭代器在插入或擦除时没有失效的事实(除非你删除那个特定的迭代器)并重新组织你的结构:

std::list<Test> list;
std::unordered_map<int, std::list<Test>::iterator> map;

map.insert(std::make_pair(101, 
    list.insert(list.end(), t1)));
map.insert(std::make_pair(102, 
    list.insert(list.end(), t2)));
map.insert(std::make_pair(103, 
    list.insert(list.end(), t3)));

通过这种方式,您的地图查找可以准确地为您提供所需内容:list iterator

答案 1 :(得分:1)

虽然巴里的方法很好,但还有另外一种方法,更为先进和复杂。您可以将数据对象,(整数)键和所有簿记位放在一块内存中。因此,数据局部性将得到改善,对内存分配器的压力将更小。例如,使用boost::intrusive

#include <boost/intrusive/list.hpp>
#include <boost/intrusive/unordered_set.hpp>
#include <array>

using namespace boost::intrusive;

class Foo {
    // bookkeeping bits
    list_member_hook<> list_hook;
    unordered_set_member_hook<> set_hook;

    const int key;
    // some payload...

public:
    // there is even more options to configure container types
    using list_type = list<Foo, member_hook<Foo, list_member_hook<>, &Foo::list_hook>>;
    using set_type = unordered_set<Foo, member_hook<Foo, unordered_set_member_hook<>, &Foo::set_hook>>;

    Foo(int key): key(key) {};
    bool operator ==(const Foo &rhs) const {
        return key == rhs.key;
    }
    friend std::size_t hash_value(const Foo &foo) {
        return std::hash<int>()(foo.key);
    }
};

class Bar {
    Foo::list_type list;

    std::array<Foo::set_type::bucket_type, 17> buckets;
    Foo::set_type set{Foo::set_type::bucket_traits(buckets.data(), buckets.size())};

public:
    template<typename... Args>
    Foo &emplace(Args&&... args) {
        auto foo = new Foo(std::forward<Args>(args)...);
        // no more allocations
        list.push_front(*foo);
        set.insert(*foo);
        return *foo;
    }
    void pop(const Foo &foo) {
        set.erase(foo);
        list.erase(list.iterator_to(foo));
        // Lifetime management fun...
        delete &foo;
    }
};

int main() {
    Bar bar;
    auto &foo = bar.emplace(42);
    bar.pop(foo);
}

衡量两种算法对数据的好坏程度。我的想法可能只会给你更多的代码复杂性。