你该如何使用容器

容器的使用

在 main.cpp 文件与打印结果都太长了,这一篇文章我把他们放在了文章最后,文章后半段有容器结构图、完整代码、打印结果
在编程中,我们总需要处理一些数据,当数据的数量变得庞大之后,不同存储数据的方式将会影响到处理数据的速度,而且这个影响是非常大的,我们需要注意使用合适的数据结构对数据进行储存
在 main.cpp 文件中,我列出了 STL 标准库的常用容器用法,他们的一些简单用法足以覆盖平时的使用
在这里我还是要强调一下新手应该优先考虑使用 vector 容器,如果你的数据需要特殊处理,在考虑使用其他更适合的容器
在最后 map set 这两个容器产生了很多变体,但实际是都用处不大,他们的区别是底层数据存储是红黑树还是哈希表(hash table),使用最多的容器还是 map set unordered_map 这三个容器
据说STL实现的容器性能不佳,如果真的遇到了瓶颈还需要引入第三方容器
接下在我会简单讲一讲什么容器该怎么用,希望能让你认识到几种容器的作用与优缺点

合适的容器

线性容器

在容器上,我喜欢将 array vector deque 容器称为线性容器,因为他们是数据连续的,可以通过下标快速的访问,经过 deque 实际是分开存储的,但是在逻辑上依旧是线性数据
在这些线性容器上,最明显的特点就是遍历快,添加快,访问快,但是缺点也很明显,删除数据时,需要移动附近的数据到被删除的部分,以填住这个空缺,因为他们要保持数据连贯性
最明显的例子就是 vector ,当删除第一个时,需要将第二个放入第一个的位置,第三个放入第二个的位置,以此类推,删除一个就要移动所有数据
尽管如此,我也是推荐优先使用 vector 容器,它完全可以代替普通数组和 array 的使用,我推荐你优先使用 vector 容器,因为他可以动态扩展容量
那 array 什么该使用了,当你知道数据的个数一定不变时才推荐你使用,比如我要保存 108 好汉的名字,要声明 4 大名著的书名等数量不变的东西时,才建议你使用 array ,因为只有这时 array 比 vector 生成快的优先才能覆盖掉容量不可变的劣势
deque 则是解决 vector 首部插入很慢的问题,它可以让两端都可以快速插入,但是如果从中间插入则速度也会变慢,所以当你总是需要从两则删除或者插入时推荐你使用 deque,如果只是单边的话,比如总是从第一个位置插入,使用反向的 vector 会更快

队列和栈

queue stack 被称为队列和栈,他们本身是封装了 deque 容器,他们只是约束了 deque 的使用
deque 有两边插入删除都快的特性,所以这两个容器都利用了这一点来制造约束
queue 队列是先进先出,从一边进,另一边出的模式,它约束了类似排队的机制,使用者只能观察容器的两端,中间的数据是看不见的,这个容器在网络编程中十分常见,可以让网络客户端先来后到的接入,正是因为它可以排队的特性
stack 栈是先进后出,从顶部进,顶部出,一个被三面包围的容器,只有一个口可以进出,常用来做数据嵌套,可以保持数据与本层一致,返回时弹出又会显示出上一层数据,常用于做撤销返回功能,因为它记录了上一次做了什么

链表

链表也是一个相当常用的容器,list forward_list 两个链表的数据是通过指针去指向下一个数据的,也就是说还没有指针跳转之前是不知道数据在那你的,每访问下一个数据都需要跳转指针,所以遍历的数据会很慢
所以当你使用指针时,你大概是不需要遍历的,而是需要知道数据所在指针位置,通过指针去向前向后移动
链表的好处在于,插入和删除都很快,但是遍历和查找和随机访问都非常慢,所以链表应该储存需要反复修改的数据,且不需要经常遍历的数据

查找容器
1 2 4 8 16 // 5次
32 64 128 256 512 // 10次
1024 2048 4096 8192 16384 // 15次
32768 65536 131072 262144 524288 // 20次

map set unordered_map 等结构类型的容器都是为了查找而生的,他们的优点就是找的快
使用 map 时,你的数据总是散的,一个一个互不相干,然后你总是需要查找,通过 key 去找到 value 这种用法,比如将一个万人学校的人名字存入 map ,它仅需要十几次跳转就可以找到,如果存入 vector 可能需要几千上万次跳转
map 是二叉树数据结构,它可以让数据指数型上升的进行有序排列,如果数据是一万,在二叉树看来只需要查到 8192 这个阶段,查 9 次就可以覆盖一万的数据,查到 524288 时为 20 次,此时的覆盖范围是 524288*2-1 的数据,已经达到百万级别,这意味这百万数据也只需要查询 20 次,与长度为 20 的链表的遍历速度基本一致
unordered_map 对比 map 胜在更快,unordered_map 是哈希结构(hash),为什么不命名为 hash_map 我也很迷惑
unordered_map 的优势是大数据下的查询,因为使用 hash 计算随机值,可以一次找到目标所在位置,然后深入查找,但如果深入查找时链条很深,就会导致效率也成指数级降低不低 map 的指数查找
之所以 unordered_map 在大数据下可以比 map 快是因为 map 的启动很长慢,map 启动到 十万数据就需要 17 次的查询,这些低速区就是 map 在大数据下不如 unordered_map 的原因,因为 unordered_map 通常一次就跳转到指定位置,只要深度不超过 10 个,那几乎就是更快的
在 map 代码的后面我列举了 multimap unordered_multimap 等一系列的 map set 容器变体,实际上你只需要关心是否需要重复就可以了,因为 map 可以覆盖 90% 的查找容器需求

操作时间

我在 main.cpp 文件的最好部分,编写了一些对容器的计算操作,让各种容器的优缺点进行对比,让你更直观的感受到用对或者用错容器参审的性能差异
这些容器的使用总是有优缺点的,有时候需求是复杂的,可能也是冲突的,你要衡量到底应该如果使用这些容器,或者是组合使用他们

容器结构图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码演示

main.cpp
#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include <stack>
#include <forward_list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <chrono>

#include "mclog.h"

// 普遍版本转字符串
template <typename T>
std::string to_str(const T &val)
{
    return std::to_string(val);
}

// 重载版本,不处理 std::string 类型
std::string to_str(const std::string &str)
{
    return str;
}

// 重载版本,处理 pair 键值对版本,引用普遍版本转字符串
template <typename T1, typename T2>
std::string to_str(const std::pair<T1, T2> &val)
{
    return "[" + to_str(val.first) + ": " + to_str(val.second) + "]";
}

// 格式化打印
template <typename T>
void print_cont(const T &con, std::string label = "")
{
    // 使用快捷遍历方式遍历
    // 该方法需要存在前进迭代器,依旧返回的对象可直接使用
    // 使用 to_str 将 ele 的数据转为字符串
    std::cout << "| label: " << label << std::endl;
    for (auto &ele : con)
    {
        std::cout << "| " << to_str(ele) << " ";
    }
    std::cout << std::endl
              << std::endl;
}

int main(int argc, char **argv)
{
    // array
    {
        // 特点分析
        // 和 int buf[5] 普通数组效果一致,需要在初始化时指定数组空间大小
        // 使用 array 比普通数组要方便,支持判空和反向迭代器等
        // 空间是固定的,需要扩容可以新建一个
        // 支持下标遍历
        MCLOG("使用 array 容器")

        std::array<int, 5> arr{101, 202, 303, 404, 505};
        MCLOG("初始化后容器大小 " $(arr.size()));

        MCLOG("下标遍历")
        for (size_t i = 0; i < arr.size(); i++)
        {
            std::cout << arr[i] << " ";
        }
        std::cout << std::endl;

        // 使用下标赋值,数组的第一个下标为 0,最后一个是size - 1
        arr[0] = 10;
        arr[arr.size() - 1] = 20;

        MCLOG("快捷遍历")
        for (auto &a : arr)
        {
            std::cout << a << " ";
        }
        std::cout << std::endl;

        MCLOG("print_cont 函数打印格式对比")
        print_cont(arr, "array 容器");
    }

    // vector
    {
        // 特点分析
        // 动态数组,可以自动扩展容量,数据是连续存放的
        // 使用上和 array 与普通数组是否相似,可以使用下标
        MCLOG("使用 vector 容器")

        std::vector<int> vec;
        MCLOG("初始化后容器大小 " $(vec.size()));

        // 赋值 5 次,从尾部赋值最快
        for (int i = 0; i < 5; i++)
        {
            vec.push_back(i + 100);
        }

        MCLOG("赋值后容器大小 " $(vec.size()));
        MCLOG("实际内存分配 " $(vec.capacity()));

        // 赋值 10 次
        for (int i = 0; i < 10; i++)
        {
            vec.push_back(i + 200);
        }
        MCLOG("第二次宽容后大小 " $(vec.size()));
        MCLOG("实际内存分配 " $(vec.capacity()));

        // 可以删除元素使 size 变小
        // 删除尾部元素,速度最快
        vec.erase(vec.end() - 1);
        MCLOG("删除一个值后大小 " $(vec.size()));
        MCLOG("实际内存分配 " $(vec.capacity()));

        print_cont(vec, "vector 容器");
    }

    // deque
    {
        // 用法与 vector 类似,可以访问下标
        // deque 储存的数据不是连续的,而是分段存储
        // 但是 deque 可以实现逻辑上的数据连续,从而可以访问下标
        // deque 可以从两侧高效的插入和删除 vector 只能在尾部高效的插入和删除
        MCLOG("使用 deque 容器")
        std::deque<int> deq;

        MCLOG("从两侧插入数据 ");
        deq.push_front(10);
        deq.push_front(11);
        deq.push_front(12);
        deq.push_back(20);
        deq.push_back(21);
        deq.push_back(22);

        MCLOG("第一个位置的数据 " << deq[0]);
        print_cont(deq, "deque 容器");
    }

    // queue
    {
        // queue 队列,继承自 deque 数据结构
        // 实际上 queue 是一种封装,而不是新的数据结构类型
        // queue 约束数据只能从尾部进,头部出,不能访问中间的数据,有先进先出的特点
        // queue 容器是封闭的,只能看第一个和最后一个数据
        MCLOG("使用 queue 容器")
        std::queue<int> que;
        for (int i = 0; i < 10; i++)
        {
            que.push(i + 100);
        }

        int val = que.front();
        MCLOG("第一个数据 " << val)

        val = que.front();
        MCLOG("没有推出的下一个数据 " << val)

        que.pop();
        val = que.front();
        MCLOG("推出之后的下一个数据 " << val)

        while (que.size() > 0)
        {
            int val = que.front();
            que.pop();
            std::cout << "| " << val << " ";
        }
        std::cout << std::endl
                  << std::endl;
    }

    // stack
    {
        // stack 栈结构,继承自 deque 数据结构
        // stack 和 queue 都是 deque 的封装,且特点与 queue 几乎相同,也是封闭的
        // 不同的是 stack 是先进后出,queue 是先进先出
        // stack 的底部是封上的,只能看见第一个顶部数据
        MCLOG("使用 stack 容器")
        std::stack<int> stk;
        for (int i = 0; i < 10; i++)
        {
            stk.push(i + 100);
        }
        int val = stk.top();
        MCLOG("第一个数据 " << val)

        val = stk.top();
        MCLOG("没有推出的下一个数据 " << val)

        stk.pop();
        val = stk.top();
        MCLOG("推出之后的下一个数据 " << val)

        while (stk.size() > 0)
        {
            int val = stk.top();
            stk.pop();
            std::cout << "| " << val << " ";
        }
        std::cout << std::endl
                  << std::endl;
    }

    // list
    {
        // 双向链表,数据存储在下一个指针指向的单独地址,数据不连续
        // 数据不是连续的,不管是实际内存地址还是逻辑地址,使用不能使用下标
        // 链表的好处是删除和添加不需要移动其他数据,只需要移动本身
        MCLOG("使用 list 容器")
        std::list<int> ls;
        for (int i = 0; i < 5; i++)
        {
            ls.push_back(i + 100);
        }
        MCLOG("赋值后容器大小 " $(ls.size()));

        // 无法通过下标查询,需要通过迭代器查询
        // 前进3步
        auto go = ls.begin();
        for (int i = 0; i < 3; i++)
        {
            go++;
        }
        MCLOG("前进3步 " $(*go));

        // 后退1步
        go--;
        MCLOG("后退1步 " $(*go));

        // 删除只能通过迭代器
        ls.erase(go);

        MCLOG("删除后容器大小 " $(ls.size()));
        print_cont(ls, "list 容器");
    }

    // forward_list
    {
        // 单向链表,比 list 的双向链表要快,但是无法后退
        MCLOG("使用 forward_list 容器")
        std::forward_list<int> fls;
        for (int i = 0; i < 5; i++)
        {
            fls.push_front(i + 100);
        }

        // forward_list 是单向列表,只能向前访问下一位,无法后退
        // 前进3步
        auto go = fls.begin();
        for (int i = 0; i < 3; i++)
        {
            go++;
        }
        MCLOG("前进3步 " $(*go));

        print_cont(fls, "forward_list 容器");
    }

    // map
    {
        // 红黑树树结构(red-black-tree)数据总是可以通过key值快速找到
        MCLOG("使用 map 容器")
        std::map<int, std::string> mp;
        for (int i = 0; i < 5; i++)
        {
            mp.emplace(i + 100, std::string("value" + std::to_string(i)));
        }

        // 下标法访问,添加一个值
        mp[10] = "value10";

        // 添加一个值,键值对的方式,很麻烦不推荐
        std::pair<int, std::string> pr(20, "value20");
        mp.insert(pr);

        MCLOG("获取一个值-存在 " << $(mp[10]))

        // 使用下标法访问时,不存在时会自动添加一个空的新值
        MCLOG("获取一个值-不存在 " << $(mp[30]))

        // 使用 at 函数获取,不存在会抛异常,但不会推荐新值
        try
        {
            mp.at(40);
        }
        catch (...)
        {
            MCLOG("出现异常,不存在 40 这个 key")
        }

        // 添加已存在的值为替换
        mp[10] = "new value 10";
        MCLOG("替换值 " << $(mp[10]))

        // 查找值,判断返回值是否有效,通过迭代器范围确认
        auto it = mp.find(10);
        if (it != mp.end())
        {
            MCLOG("存在 " << it->first)
        }
        else
        {
            MCLOG("不存在 " << it->first)
        }

        // 查找值,不存咋
        it = mp.find(1000);
        if (it != mp.end())
        {
            MCLOG("存在 " << it->first)
        }
        else
        {
            MCLOG("不存在 " << it->first)
        }

        MCLOG("赋值后容器大小 " $(mp.size()));
        print_cont(mp, "map 容器");
    }

    // multimap
    {
        // 是 map 的变体,可以存储多个key相同的数据
        // 因为 key 存在多个数据,下标不可用
        MCLOG("使用 multimap 容器")
        std::multimap<int, std::string> mmp;

        mmp.emplace(10, "v10");
        mmp.emplace(11, "v11");
        mmp.emplace(12, "v12");

        mmp.emplace(10, "v10-1");
        mmp.emplace(10, "v10-2");
        mmp.emplace(10, "v10-3");

        // 遍历 key 下的值
        auto range = mmp.equal_range(10);
        for (auto it = range.first; it != range.second; it++)
        {
            std::cout << to_str(*it) << " ";
        }
        std::cout << std::endl;

        auto it = mmp.find(10);
        MCLOG("存在10吗 " << (it != mmp.end()))

        it = mmp.find(200);
        MCLOG("存在200吗 " << (it != mmp.end()))

        print_cont(mmp, "multimap 容器");
    }

    // 总结 map set 的多种容器变体
    // unordered 代表 hash 结构, mul 代表可以可重复存储数据
    // 没有前缀代表不可重复的红黑树类型
    {
        // 红黑树,不可重复,带键值对 key - value
        std::map<int, std::string> mp;

        // 红黑树,可重复
        std::multimap<int, std::string> mmp;

        // 哈希表(hash),不可重复
        std::unordered_map<int, std::string> ump;

        // 哈希表,可重复
        std::unordered_multimap<int, std::string> ummp;

        // 红黑树,不可重复,只有值 value
        std::set<int> st;

        // 红黑树,可重复
        std::multiset<int> mst;

        // 哈希表,不可重复
        std::unordered_set<int> ust;

        // 哈希表,可重复
        std::unordered_multiset<int> umst;
    }

    MCLOG("容器的费时测试")

    // 定义数量一百万的值
    int size_million = 100 * 10000;

    // 定义 fn_time_calc 匿名函数(lambda)
    // 这个函数会计算一个函数运行的时间
    using namespace std::chrono;
    auto fn_time_calc = [](std::string label = "", std::function<void()> fn = nullptr) -> void
    {
        auto begin = steady_clock::now();
        if (fn)
        {
            fn();
        }
        auto end = steady_clock::now();
        auto time = duration_cast<nanoseconds>(end - begin).count();
        auto interval = std::to_string(time) + "ns";
        MCLOG($(interval) $(label))
    };

    // 总是操作首位数据,推荐 deque
    {
        std::vector<int> vec;
        std::deque<int> deq;
        for (int i = 0; i < size_million; i++)
        {
            vec.push_back(i);
            deq.push_back(i);
        }

        // 当你总是需要从第一个位置插入时
        fn_time_calc("vector 尾部插入费时 ", [&]()
                     { vec.push_back(100); });

        fn_time_calc("vector 首部插入费时 ", [&]()
                     { vec.insert(vec.begin(), 100); });

        fn_time_calc("deque 首部插入费时 ", [&]()
                     { deq.push_front(100); });

        // 当你总是需要从第一个移除时
        fn_time_calc("vector 首部移除费时 ", [&]()
                     { vec.erase(vec.begin()); });

        fn_time_calc("deque 首部移除费时 ", [&]()
                     { deq.erase(deq.begin()); });
    }

    // 总是操作中间数据,推荐 list
    {
        std::deque<int> deq;
        std::list<int> ls;
        for (int i = 0; i < size_million; i++)
        {
            ls.push_back(i);
            deq.push_back(i);
        }

        // 迭代器停留在中间位置
        // list 很慢,deque 很快
        auto it_ls = ls.begin();
        auto it_deq = deq.begin();
        fn_time_calc("list 到达中间位置费时 ", [&]() {
            for (int i = 0; i < ls.size() / 2; i++)
            {
                it_ls++;
            } 
        });

        fn_time_calc("deque 到达中间位置费时 ", [&]() { 
            it_deq = deq.begin() + (deq.size() / 2); 
        });

        // 当你总是需要从中间位置插入时
        fn_time_calc("list 插入中间位置费时 ", [&]() {
            for(int i=0;i<10;i++)
            {
                ls.insert(it_ls,i);
            } 
        });

        fn_time_calc("deque 插入中间位置费时 ", [&]() {  
            for(int i=0;i<10;i++)
            {
                deq.insert(it_deq,i);
            } 
        });
    }

    // 总是在查数据,推荐 unordered_map
    // 需要注意的是 map 总是稳定的,unordered_map 时快时慢
    {
        std::vector<std::string> vec;
        std::map<std::string, int> mp;
        std::unordered_map<std::string, int> ump;

        for (int i = 0; i < size_million; i++)
        {
            vec.push_back(std::to_string(i));
            mp.emplace(std::to_string(i), i);
            ump.emplace(std::to_string(i), i);
        }

        mp.emplace("hello", 100);
        ump.emplace("hello", 100);
        vec.push_back("hello");

        // 查数据
        fn_time_calc("map 查数据速度费时", [&]()
                     { int val = mp["hello"]; });

        fn_time_calc("unordered_map 查数据速度费时", [&]()
                     { int val = ump["hello"]; });

        fn_time_calc("vector 查数据速度费时", [&]() {
            int val = -1;
            for (int i = 0; i < vec.size(); i++)
            {
                if (vec[i] == "hello")
                {
                    val = i;
                    break;
                }
            } 
        });
    }

    return 0;
}
打印结果
使用 array 容器 [/home/red/open/github/mcpp/example/13/main.cpp:63]
初始化后容器大小 [arr.size(): 5]  [/home/red/open/github/mcpp/example/13/main.cpp:66]
下标遍历 [/home/red/open/github/mcpp/example/13/main.cpp:68]
101 202 303 404 505 
快捷遍历 [/home/red/open/github/mcpp/example/13/main.cpp:79]
10 202 303 404 20 
print_cont 函数打印格式对比 [/home/red/open/github/mcpp/example/13/main.cpp:86]
| label: array 容器
| 10 | 202 | 303 | 404 | 20 

使用 vector 容器 [/home/red/open/github/mcpp/example/13/main.cpp:95]
初始化后容器大小 [vec.size(): 0]  [/home/red/open/github/mcpp/example/13/main.cpp:98]
赋值后容器大小 [vec.size(): 5]  [/home/red/open/github/mcpp/example/13/main.cpp:106]
实际内存分配 [vec.capacity(): 8]  [/home/red/open/github/mcpp/example/13/main.cpp:107]
第二次宽容后大小 [vec.size(): 15]  [/home/red/open/github/mcpp/example/13/main.cpp:114]
实际内存分配 [vec.capacity(): 16]  [/home/red/open/github/mcpp/example/13/main.cpp:115]
删除一个值后大小 [vec.size(): 14]  [/home/red/open/github/mcpp/example/13/main.cpp:120]
实际内存分配 [vec.capacity(): 16]  [/home/red/open/github/mcpp/example/13/main.cpp:121]
| label: vector 容器
| 100 | 101 | 102 | 103 | 104 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 

使用 deque 容器 [/home/red/open/github/mcpp/example/13/main.cpp:132]
从两侧插入数据  [/home/red/open/github/mcpp/example/13/main.cpp:135]
第一个位置的数据 12 [/home/red/open/github/mcpp/example/13/main.cpp:143]
| label: deque 容器
| 12 | 11 | 10 | 20 | 21 | 22 

使用 queue 容器 [/home/red/open/github/mcpp/example/13/main.cpp:153]
第一个数据 100 [/home/red/open/github/mcpp/example/13/main.cpp:161]
没有推出的下一个数据 100 [/home/red/open/github/mcpp/example/13/main.cpp:164]
推出之后的下一个数据 101 [/home/red/open/github/mcpp/example/13/main.cpp:168]
| 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 

使用 stack 容器 [/home/red/open/github/mcpp/example/13/main.cpp:186]
第一个数据 109 [/home/red/open/github/mcpp/example/13/main.cpp:193]
没有推出的下一个数据 109 [/home/red/open/github/mcpp/example/13/main.cpp:196]
推出之后的下一个数据 108 [/home/red/open/github/mcpp/example/13/main.cpp:200]
| 108 | 107 | 106 | 105 | 104 | 103 | 102 | 101 | 100 

使用 list 容器 [/home/red/open/github/mcpp/example/13/main.cpp:217]
赋值后容器大小 [ls.size(): 5]  [/home/red/open/github/mcpp/example/13/main.cpp:223]
前进3步 [*go: 103]  [/home/red/open/github/mcpp/example/13/main.cpp:232]
后退1步 [*go: 102]  [/home/red/open/github/mcpp/example/13/main.cpp:236]
删除后容器大小 [ls.size(): 4]  [/home/red/open/github/mcpp/example/13/main.cpp:241]
| label: list 容器
| 100 | 101 | 103 | 104 

使用 forward_list 容器 [/home/red/open/github/mcpp/example/13/main.cpp:248]
前进3步 [*go: 101]  [/home/red/open/github/mcpp/example/13/main.cpp:262]
| label: forward_list 容器
| 104 | 103 | 102 | 101 | 100 

使用 map 容器 [/home/red/open/github/mcpp/example/13/main.cpp:270]
获取一个值-存在 [mp[10]: value10]  [/home/red/open/github/mcpp/example/13/main.cpp:284]
获取一个值-不存在 [mp[30]: ]  [/home/red/open/github/mcpp/example/13/main.cpp:287]
出现异常,不存在 40 这个 key [/home/red/open/github/mcpp/example/13/main.cpp:296]
替换值 [mp[10]: new value 10]  [/home/red/open/github/mcpp/example/13/main.cpp:301]
存在 10 [/home/red/open/github/mcpp/example/13/main.cpp:307]
不存在 8 [/home/red/open/github/mcpp/example/13/main.cpp:322]
赋值后容器大小 [mp.size(): 8]  [/home/red/open/github/mcpp/example/13/main.cpp:325]
| label: map 容器
| [10: new value 10] | [20: value20] | [30: ] | [100: value0] | [101: value1] | [102: value2] | [103: value3] | [104: value4] 

使用 multimap 容器 [/home/red/open/github/mcpp/example/13/main.cpp:333]
[10: v10] [10: v10-1] [10: v10-2] [10: v10-3] 
存在10吗 1 [/home/red/open/github/mcpp/example/13/main.cpp:353]
存在200吗 0 [/home/red/open/github/mcpp/example/13/main.cpp:356]
| label: multimap 容器
| [10: v10] | [10: v10-1] | [10: v10-2] | [10: v10-3] | [11: v11] | [12: v12] 

容器的费时测试 [/home/red/open/github/mcpp/example/13/main.cpp:390]
[interval: 567ns] [label: vector 尾部插入费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 181332ns] [label: vector 首部插入费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 702ns] [label: deque 首部插入费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 119053ns] [label: vector 首部移除费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 1372ns] [label: deque 首部移除费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 2591838ns] [label: list 到达中间位置费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 1211ns] [label: deque 到达中间位置费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 1120ns] [label: list 插入中间位置费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 3587019ns] [label: deque 插入中间位置费时 ]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 1475ns] [label: map 查数据速度费时]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 1429ns] [label: unordered_map 查数据速度费时]  [/home/red/open/github/mcpp/example/13/main.cpp:408]
[interval: 11082564ns] [label: vector 查数据速度费时]  [/home/red/open/github/mcpp/example/13/main.cpp:408]

项目路径

https://github.com/HellowAmy/mcpp.git
~~~![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/019c8c04cd7142c5952a14d2610edee2.png#pic_center)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐