c++ STL六大组件之一 -- 容器 vector/deque/list/set/map/unordered_map 原理、用法详解

vector

​ vector 应该是最常用的容器了,vector 即动态数组, 与 string 类似,vector 成员物理上也是连续存放的,支持随机迭代器。

内存机制:

在这里插入图片描述

​ vector 可自动扩充内存, vector 自动扩容并不是每次超过扩充一个成员的内存,而是将容量扩充为原来容量大小的 2 倍,扩充后的容量为: vector.capacity() = vector.capacity() * 2。

​ 同时, vector 在扩容时会开辟一块新的较大内存,再将原有的成员复制到新的内存块,这会带来较大的性能。

vector<int> vec;
for(int i = 1; i < 10; ++i)
{
    vec.push_back(i);
		cout << "vec.size: " << vec.size() << ", vec.capacity: " << vec.capacity() << endl;
}

由下图所知:vector扩容大小为 2 的倍数。在这里插入图片描述

vector 常用 API

用法解释
reserve()当我们事先可以预估所需要存储的容量大小时可以首先调用 reserve() 预先分配内存,可以降低vector 扩容带来的性能损耗。
data()获取指向元素本身的指针
front()/back()返回 vector 中第一个/最后一个元素
begin()/end()返回指向第一个元素/最后一个元素的后一内存单元的迭代器
insert()/erase()可在 vector 任意位置插入/删除一个元素,参数:pos
push_back()/pop_back()插入一个元素在末尾/删除末尾元素
emplace()/emplace_back()构造成员对象,并插入所需插入位置,参数:pos, Args
resize()重新分配内存大小,同样是按照 2 的倍数进行分配内存。

​ 在使用 vector 常犯的错误就是没有考虑迭代器失效的问题。当我们删除元素节点时, 可能会触发底层的内存收缩操作,使得 vector 会另选一块较小的内存块,而先前使用的迭代器指向的内存块以及是原先分配的内存块,导致迭代器失效。如下例代码所示:


vector<int> vec {1, 2, 3};
	cout << "size: " << vec.size() << " capacity: "<< vec.capacity() << endl;
	for(vector<int>::iterator ite = vec.begin(); ite != vec.end(); )
	{
		// 1:错误版本
		//vec.erase(ite);
		//++ite;
		//2: 正确使用方式,注意 for循环中不需要执行 ++ite,否则会导致越界问题,有兴趣可以试试
		ite = vec.erase(ite);
	}
	cout << "size: " << vec.size() << " capacity: "<< vec.capacity() << endl;

使用第一种方法删除元素,导致迭代器失效,内存非法访问:
在这里插入图片描述

deque

​ deque 的意思就是 double - end queue,即双端队列。双端队列可以在队列的首端和未端任意的添加和删除元素。

​ deque 的接口与 vector 相比有以下不同:

​ deque 有 pop_front() 、push_front() 和 emplace_front() 接口用于在队列首端插入或删除元素;

​ deque 没有 data()、capacity() 和 reserve() 接口。

​ deque 内存布局并非完全连续,而是由固定大小的数组序列组成,一般如下:

在这里插入图片描述

list

​ list 即双向链表,内存布局不连续。可以以 o(1) 的复杂度在中间任意位置插入或删除元素节点,和 vector 相比, 无须大量移动元素节点。

在这里插入图片描述

​ list 较不同的接口如下:

​ merge():合并两个有序的 list,,示例如下:

std::list<int> list1 = { 5,10,2,4,3 };
std::list<int> list2 = { 9,7,2,3,5,4 };

list1.sort();
list2.sort();

list1.merge(list2);

​ splice(): 将元素从一个 list 转移到另一个 list,示例如下:

#include<iostream>
#include<list>
using namespace std;
ostream& operator<<(ostream& ost, list<int>& list)
{
    for(auto & i : list)
    {
        ost << i <<" ";
    }
    return ost;
}

int main()
{
    list<int> list1{1, 2 ,3, 4};
    list<int> list2{5, 6, 7, 8};
    cout << "list1: " << list1 << endl;
    cout << "list2: " << list2 << endl;
		//在 list1 的第一个位置迁移 list2 的前两个元素;
    list1.splice(list1.begin(), list2, list2.begin(), ++++list2.begin());
    cout << "after splice: " << endl;
    cout << "list1: " << list1 << endl;
    cout << "list2: " << list2 << endl;

    return 0;
}

set / unordered_set

​ set 和 map 是关联性容器,关联容器实现了可快速搜索的排序数据结构(复杂度O(log n))。而 vector、list 、deque、array属于序列容器,实现了可以顺序访问的数据结构。

​ set 和 map 底层是由红黑树实现,红黑树是一种近似平衡二叉树,可以以 o(log n ) 的复杂度实现快速查找,元素间有序;

unordered_set 和 unordered_map 底层是由哈希表实现(空间换时间),可以以o(1) 的复杂度实现快速查找,但是元素间无序。

​ set 不能存放重复的 value 的节点,如果需要存放重复的 value 节点 ,可以使用 multiset 和 unordered_multiset。

set 常用 API

begin() / end()返回指向第一个元素的迭代器 / 返回集合最后一个元素后面的元素的迭代器
insert(value) /erase()在集合任意位置插入一个元素节点,如果集合中已经拥有该key值的节点,将会插入失败
find(value)在集合中,查找对应的节点,并返回对应的迭代器
// insert   
set<int> setInt{1, 3, 5, 4};
pair<set<int>::iterator, bool > result_1 = setInt.insert(1);
cout << * result_1.first << ", " << (result_1.second ? "insert successful." : "insert unsuccessful") << endl;
pair<set<int>::iterator, bool > result_2 = setInt.insert(2);
cout << * result_2.first << ", " << (result_2.second ? "insert successful." : "insert unsuccessful") << endl;

在这里插入图片描述

//set 有序, unordered_set 无序
set<int> setInt{2, 4, 5, 1, 3};
cout << "setInt output: ";
for(auto & i : setInt)
{
    cout << i << " ";
}
cout << endl;
unordered_set<int> setInt2{2, 4, 5, 1, 3};
cout << "unordered_set  output: ";
for(auto & i : setInt2)
{
    cout << i << " ";
}
cout << endl;

在这里插入图片描述

multiset

cout << "multiset: ";
multiset<int> setMulti{1, 2, 1, 3, 5, 3};
for(auto & i : setMulti)
{
    cout << i << ", ";
}
cout << endl;

在这里插入图片描述

map / unordered_map

​ map 的底层实现以及接口和 set 类似。但是 map 属于字典类型, 需要存放 <key, value> 的键值对。同时 map 支持下标访问:map[key] = value。

// map 常见用法
struct compare
{
    bool operator()(const string& lhs, const string& rhs) const
    {
        return lhs.size() > rhs.size();
    }
};

int main()
{
    map<string, int, compare> Map{{"1", 1}, {"12", 2}, {"123", 3}};
    for(auto & ref : Map)
    {
        cout << " first: " << ref.first << " second: " << ref.second << endl;
    }
  	
  	//查找
    auto ite = Map.find("1");
    if(ite != Map.end())
    {
        cout << "first: " << ite->first << " second: " << ite->second << endl;
    } 
    return 0;
}

unordered_map 用法,自定义哈希函数:


struct Hash
{
    size_t operator()(const pair<int, int> & key) const
    {
        size_t h1 = std::hash<int> {}(key.first);
        size_t h2 = std::hash<int> {}(key.second);
        return h1 ^ (h2 << 1); // or use boost::hash_combine
    }
};

int main()
{
    unordered_map<pair<int, int>, int, Hash> Map{{make_pair(1, 2), 1}, 
                                                 {make_pair(1, 3), 1}, {make_pair(1, 4), 1}};
    
    for(auto & ite : Map)
    {
        cout << "key.first: " << ite.first.first << ", key.second: "
        << ite.first.second << ", value: " << ite.second << endl;
    }

    return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
container Collection 标记: 顶级接口 List 标记: interface ArrayList 标记: class CRUD : boolean add​(E e) boolean remove​(Object o) E set​(int index, E element) E get​(int index) 底层数组实现,查询快,增删慢 LinkedList 标记: class CRUD : boolean add​(E e) E get​(int index) 底层为链表,增删快,查询快 Vector 标记: class 底层数组实现,线程安全,速度太慢,没用 CopyOnWriteArrayList 标记: class Set 标记: interface HashSet 标记: class CRUD : boolean add​(E e) boolean remove​(Object o) 底层哈希表,基于hashCode的equals的比较方式,线程不安全,存取速度快。 SortedSet 标记: interface TreeSet 标记: class 实现comparable接口,元素是以二叉树的形式存放的。线程不安全 CopyOnWriteArraySet 标记: class Queue 标记: interface BlockingQueue 标记: interface ArrayBlockingQueue LinkedBlockingQueue ConcurrentLinkedQueue 标记: class Map 标记: 顶级接口 HashMap 标记: class V get​(Object key) V put​(K key, V value) Set<K> keySet​() Set<Map.Entry<K,V>> entrySet​() 线程不安全,速度快,允许存放null键,null值。 SortedMap 标记: class TreeMap 对键进行排序 HashTable 标记: class Properties 标记: class 线程安全,速度慢,不允许存放null键,null值,已被HashMap替代。 Collections 标记: 均以synchronized实现, 性能没用提高 synchronizedCollection synchronizedList synchronizedSet synchronizedMap synchronizedSortedSet synchronizedSortedSet JUC CopyOnWriteArrayList 写有锁,读无锁,读写之间不阻塞,优于读写锁 写入时先copy一个容器副本,再添加新元素,最后替换引用 copy的容器副本过大时,速度慢,不易使用 CopyOnWriteArraySet 底层使用CopyOnWriteArrayList实现 使用addIfAbsent()添加元素时,会遍历数组,如果存在元素,则抛弃副本 ConcurrentHashMap 初始容量默认为16段(segment),使用分段锁设计 ConcurrentLinkedQueue 高并发下性能最好的队列 无锁,采用CAS比较算法,核心参数(V,E,N) V:要更新的变量 E:预期值 N:新值 只有当V==E时,V=N;否则表示已被更新过,则取消当前操作 阻塞队列 BlockingQueue接口 void put(E e) 将指定元素插入此队列,如果没有可用空间,则等待 E take()获取并移除此队列头部元素,如果没有可用元素,则等待 ArrayBlockingQueue 数组结构实现,有界队列,手工固定上限 LinkedBlockingQueue 链表结构实现,无界队列(默认上限Integer.MAX_VALUE)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值