关联容器(第3章)(条款23)

条款23:考虑用排序的vector替代关联容器

标准关联容器通常实现为平衡的二叉查找树,它适合如下应用场景:频繁的插入、删除,查找等操作,这些操作基本都在同步交叉进行(以下简称场景一)。

但是很多应用程序使用数据结果的方式并没有这么混乱,它们使用的过程明显的分为3个阶段(以下简称场景二):

1)设置阶段:创建一个新的数据结构,并插入大量数据,这个阶段几乎所有的操作都是插入和删除操作几乎没有查找操作,有也很少;

2)查找阶段:查询该数据结构特定信息,这个阶段几乎所有的操作都是查询操作几乎没有插入和删除操作,有也很少;

3)重组阶段:改变数据结构内容,或许是删除当前数据,插入新数据在行为上与第一个阶段类似,当这个阶段结束后,重新进入第二阶段;

针对上面描述的两种场景,结论如下:

场景一适合使用关联容器

场景二适合使用 vector 存储,排序后使用二分查找法 binary_search 查找:因为这样更节省内存(没有树节点额外的指针),且查询可能更快(跨页面更少)

200万int数据,分别采用set / vector / unordered_set进行测试,结果如下,通过数据可以看到,场景二使用vector先排序,然后二分查找的方案确实比关联容器set快,甚至比unordered_set散列容器都快,但是set和unordered_set本身在两种场景中的使用差距不大....,另外特别注意,场景一完全不适合用vector的方案,看,我写博客的时候程序还没运行完成

容器 场景 耗时(milliseconds)
set 场景一 6675
set 场景二 6845
unordered_set 场景一 7655
unordered_set 场景二 7613
vector 场景一 截止我写博客的时候还没运行完.....
vector 场景二 2867

我的测试代码

    void test_23() {
        const int N = 2000000;
        // 一次性插入数据,然后查找的场景
        std::default_random_engine generator;
        std::uniform_int_distribution<int> distribution(0, N);
        Common::TimeClock tc;  // 时间打点
        set<int> iset1;
        for (int i = 0; i < N; ++i) {
            iset1.insert(distribution(generator));
        }
        int findCount = 0;
        for (int i = 0; i < N; ++i) {
            findCount += iset1.count(i);
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();

        // 频繁插入、查找数据场景
        tc.start();
        set<int> iset2;
        findCount = 0;
        for (int i = 0; i < N; ++i) {
            // 不断插入和查找数据
            iset2.insert(distribution(generator));
            findCount += iset2.count(i);
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();
    }
   void test_23() {
        const int N = 2000000;

        // 一次性插入数据,然后查找的场景
        std::default_random_engine generator;
        std::uniform_int_distribution<int> distribution(0, N);
        Common::TimeClock tc;
        vector<int> ivec1;
        ivec1.reserve(N);
        for (int i = 0; i < N; ++i) {
            ivec1.push_back(distribution(generator));  // 插入数据
        }
        std::sort(ivec1.begin(), ivec1.end()); // 排序
        int findCount = 0;
        for (int i = 0; i < N; ++i) {
            findCount += std::binary_search(ivec1.cbegin(), ivec1.cend(), i); // 二分查找
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();

        // 频繁插入、查找、删除数据场景
        tc.start();
        vector<int> ivec2;
        ivec2.reserve(N);
        findCount = 0;
        for (int i = 0; i < N; ++i) {
            ivec2.push_back(distribution(generator));
            findCount += std::find(ivec2.cbegin(), ivec2.cend(), i) != ivec2.end();
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();
    }

另外测试了map / vector / unordered_map的场景二,vector略高于map,可能是pair对的原因,效果没有set对边明显, unordered_map明显高于另外两者

容器 场景 耗时(milliseconds)
map 场景二 18459
unordered_map 场景二 11874
vector 场景二 15776

我的测试代码

    using data = std::pair<string, int>;

    struct DataCompare {
    public:
        // 两个pair的比较
        bool operator()(const data &lhs, const data &rhs) const {
            return lhs.first < rhs.first;
        }

        // operator<(pair, string)
        bool operator()(const data &lhs, const data::first_type &rhs) const {
            return keyLess(lhs.first, rhs);
        }

        // operator(string, pair)
        bool operator()(const data::first_type &lhs, const data &rhs) const {
            return keyLess(lhs, rhs.first);
        }

    private:
        // operator<(string, string)
        bool keyLess(const data::first_type &k1, const data::first_type &k2) const {
            return k1 < k2;
        }
    };

    void test_23_1() {
        const int N = 2000000;
        // map测试一次性插入数据,然后查找的场景
        std::default_random_engine generator;
        std::uniform_int_distribution<int> distribution(0, N);
        Common::TimeClock tc;  // 时间打点
        map<string, int> imap1;
        for (int i = 0; i < N; ++i) {
            imap1.emplace(std::to_string(distribution(generator)), i);
        }
        int findCount = 0;
        for (int i = 0; i < N; ++i) {
            findCount += imap1.count(std::to_string(i));
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();

        // vector测试一次性插入数据,然后查找的场景
        tc.start();  // 时间打点
        vector<std::pair<string, int>> vec;
        vec.reserve(N);
        for (int i = 0; i < N; ++i) {
            vec.emplace_back(std::to_string(distribution(generator)), i);
        }
        findCount = 0;
        std::sort(vec.begin(), vec.end(), DataCompare());
        for (int i = 0; i < N; ++i) {
            // binary_search
            if (std::binary_search(vec.cbegin(), vec.cend(), std::to_string(i), DataCompare())) {
                ++findCount;
            }

            // lower_bound
//            auto lit = std::lower_bound(vec.begin(), vec.end(), std::to_string(i), DataCompare());
//            if (lit != vec.end() && !DataCompare()(std::to_string(i), *lit)) {
//                ++findCount;
//            }

            // upper_bound
//            auto uit = std::upper_bound(vec.begin(), vec.end(), std::to_string(i), DataCompare());
//            if (uit != vec.end() && !DataCompare()(*uit, std::to_string(i))) {
//                ++findCount;
//            }

            // equal_range
//            auto p = std::equal_range(vec.begin(), vec.end(), std::to_string(i), DataCompare());
//            if (p.first != p.second) {
//                ++findCount;
//            }
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();

        // unordered_map一次性插入数据,然后查找的场景
        tc.start();
        std::unordered_map<string, int> uimap1;
        for (int i = 0; i < N; ++i) {
            uimap1.emplace(std::to_string(distribution(generator)), i);
        }
        findCount = 0;
        for (int i = 0; i < N; ++i) {
            findCount += uimap1.count(std::to_string(i));
        }
        std::cout << "findCount: " << findCount << std::endl;
        tc.end();
    }

参考:《Effective STL中文版》

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112443484
今日推荐