stl 容器套容器 内存_哪个STL容器?

stl 容器套容器 内存

Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and when to use it.

作为C ++标准模板库(STL)的一部分,包含了通用容器的集合。 这些容器中的每个容器都有不同的用途,并且有不同的优缺点。 通常很难决定使用哪个容器以及何时使用它。

This article is a guided tour of the STL containers. It is not intended to teach you how to use each of these containers; rather, it will help you decide when and where to use each of them. It will also show you a few tips, tricks and snippets of information that are not normally documented elsewhere but may come in handy.

本文是STL容器的导览。 它无意教您如何使用这些容器中的每一个; 相反,它将帮助您决定何时何地使用它们。 它还将向您显示一些提示,技巧和信息片段,这些信息通常在其他地方没有记录,但可能会派上用场。

The target audience for this article is intermediate and above. I don't necessarily discuss every new concept I introduce but every time a new and important concept is introduced I will be sure to provide a link to a reference where you can read more if you need to.

本文的目标受众是中级以上。 我不一定要讨论引入的每个新概念,但是每次引入一个重要的新概念时,我一定会提供指向参考的链接,您可以根据需要阅读更多内容。

This article discusses the following STL containers:

本文讨论了以下STL容器:

vector

向量

deque

双端队列

list

清单

set

map

地图

multiset

多集

multimap

多图

bitset

位集

向量 (vector)

The vector is a sequence container that represents an abstraction of a dynamic one dimensional array. It manages an internal buffer which can automatically grow to accommodate the contents. The allocation strategy used to allocate the internal buffer is not defined by the C++ Standard and is dependent upon the allocator. Unless a user defined allocator is provided it will default to using a vendor specific allocator, which will provide a general allocation strategy for best case general use. Typically, the default allocator will just double the size of the internal buffer when current capacity is reached; however, different compilers may use different strategies so you are advised to consult your compilers documentation.

向量是一个序列容器 ,代表动态一维数组的抽象。 它管理一个内部缓冲区,该缓冲区可以自动增长以容纳内容。 C ++标准未定义用于分配内部缓冲区的分配策略,该策略取决于分配器 。 除非提供了用户定义的分配器,否则它将默认使用供应商特定的分配器,该分配器将为一般情况下的最佳使用情况提供通用分配策略。 通常,当达到当前容量时,默认分配器将使内部缓冲区的大小增加一倍。 但是,不同的编译器可能使用不同的策略,因此建议您查阅编译器文档。

The internal buffer of a vector is guaranteed to be binary compatible with a standard C array. This means it can safely be used with legacy code. There is no specific operator to provide a C array compatible pointer; rather, you take the address of the internal buffer as though you were taking the address of the first element in an array.

向量的内部缓冲区保证与标准C数组二进制兼容。 这意味着它可以安全地与遗留代码一起使用。 没有特定的运算符可提供与C数组兼容的指针。 相反,您采用内部缓冲区的地址,就像采用数组中第一个元素的地址一样。

An example of accessing the internal buffer of a vector
vector<int> v(10, 0); // create a vector with 10 ints, each set to 0
int * p = &v[0]; // Get a pointer to the internal dynamic array

This buffer is guaranteed to be perfectly safe to use and 100% compatible with a C style array as long as no mutable methods are called on the instance the buffer belongs to. In much the same was that mutable methods on a vector may invalidate iterators so, too, can they invalidate the internal buffer. Why is this? Well, put simply, the internal buffer may need to be reallocated to allow for extra capacity. When this happens the vector creates a whole new buffer, which will be bigger than the existing one, all data from the old buffer are copied to the new buffer and the old buffer is then destroyed. It should be clear that if this happens your C style pointer will be pointing to invalid memory.

只要该缓冲区所属的实例上没有调用任何可变方法 ,就可以保证该缓冲区使用起来非常安全,并且100%与C样式数组兼容。 向量上的可变方法可能会使迭代器无效,因此它们也可以使内部缓冲区无效。 为什么是这样? 简而言之,可能需要重新分配内部缓冲区以允许额外的容量。 发生这种情况时,向量将创建一个新的缓冲区,该缓冲区将大于现有缓冲区,然后将所有来自旧缓冲区的数据复制到新缓冲区,然后销毁旧缓冲区。 应该清楚的是,如果发生这种情况,您的C样式指针将指向无效的内存。

A specific exception to be aware of to the C array guarantee is vector<bool>, which the C++98 version of the C++ Standard specifies should be specialised such that each Boolean element only occupies one bit. There are other considerations with this specialisation too. For example, the subscript operators do not return a reference to a value within the vector, they return a copy of a bool that is constructed when the operator is called, which represents the Boolean state for the bit it represents.

要注意C数组保证的一个特殊异常是vector <bool> ,C ++ Standard的C ++ 98版本应对此进行特殊说明 ,以使每个布尔元素仅占用一位。 此专业化还有其他考虑因素。 例如, 下标运算符不返回对向量中值的引用,而是返回布尔值的副本,该布尔值是在调用该运算符时构造的,它表示其所表示位的布尔状态。

The vector class is very efficient in terms of memory usage. As well as the size of the allocated buffer it has a very small overhead in terms of additional members of the class to manage its internal state. Typically a an empty vector will utilise between 16 to 24 bytes (depending upon how it‘s implemented, which is not defined by the C++ Standard).

向量类在内存使用方面非常有效。 除了分配的缓冲区的大小以外,在管理该类内部状态的类的其他成员方面,它的开销也很小。 通常,空向量将占用16到24个字节(取决于其实现方式,而C ++标准未定义)。

The size of the buffer may very well be greater than the size of the data held within the vector. This is because the capacity of the vector (over actual size of the internal buffer) will always be at least as big as the size (the amount of the capacity in use) but will usually be greater.

缓冲区的大小可能会大于向量中保存的数据的大小。 这是因为向量的容量(超过内部缓冲区的实际大小)将始终至少与大小(使用的容量大小)一样大,但通常会更大。

When a vector needs to grow it must allocate a new buffer, copy the old to the new before destroying the old. This means that during mutable function calls it is possible that the amount of memory consumed by the vector may, for a short period, be twice as much as the previous capacity.

当向量需要增长时,必须分配一个新的缓冲区,在销毁旧缓冲区之前将其复制到新缓冲区。 这意味着在可变函数调用期间,向量在短时间内消耗的内存量可能是先前容量的两倍。

The internal buffer of a deque may not shrink when the container is cleared. This can be a problem if you have released a large quantity of data and want to reclaim the memory being used. There is a simple trick, called The Self Swapping Idiom that can be used. Basically, swap the vector to be cleared with a temporary empty one. When you use the swap method on a vector (this does not, necessarily, apply when using std::swap) all that happens is that the pointer values to each of the buffers is swapped, making a swap a very efficient process. When the other vector is destroyed all the memory it now has goes with it. Your original vector will now contain an empty buffer.

清空容器后,双端队列的内部缓冲区可能不会缩小。 如果您释放了大量数据并想回收正在使用的内存,这可能是个问题。 可以使用一个简单的技巧,称为“自我交换习惯用法”。 基本上,将要清除的向量与一个临时的空交换。 在向量上使用swap方法时(使用std :: swap时不一定适用),所有发生的事情是交换了每个缓冲区的指针值,从而使交换成为非常有效的过程。 当另一个向量被销毁时,它现在拥有的所有内存也随之消失。 您的原始向量现在将包含一个空缓冲区。

The vector is most efficient when appending items (pushing back).If you think of the vectors internal buffer as a stack of plates with the top plate being its back it’s easy to add new plate, you just put them on top. What if we want to insert a plate in the middle or at the bottom? Well, now it’s a little more complex. We have to lift up the plate to make space to insert a new one. It’s the same with a vector. If we want to insert a new item every item after it must move back to make space. The nearer the front you insert the longer it takes as the more items there are to move. Likewise, deleting anywhere other than the back of the vector is equally inefficient.

向量在追加项目(回推)时效率最高。如果您将向量内部缓冲区视为一叠板,而顶部板为后板,则很容易添加新板,只需将它们放在顶部即可。 如果我们想在中间或底部插入板子怎么办? 好吧,现在有点复杂了。 我们必须抬起盘子来腾出空间来插入新的盘子。 向量也一样。 如果我们要插入一个新项目,则必须移回每个项目以腾出空间。 您插入的前端越近,移动的物品越多,所需的时间就越长。 同样,删除除向量后部以外的任何地方同样无效。

When appending to or removing from the back of a vector the time complexity is O(1). In other words, constant time as long as the buffer does not need to be reallocated.

当附加到向量的背面或从向量的背面移除时,时间复杂度为O(1)。 换句话说,只要不需要重新分配缓冲区就可以保持恒定的时间。

If the number of elements being inserted into or removed from a vector is know beforehand the time complexity is O(N+M) otherwise the time complexity is O(NM), where N is the number of items being inserted or removed and M is the number of items that need to be moved.

如果预先知道要插入到向量中或从向量中删除的元素的数量,则时间复杂度为O(N + M),否则时间复杂度为O(NM),其中N为要插入或删除的项目数,M为需要移动的项目数。

In all cases of adding to a vector, the time complexities do not take into account the fact that the vector may also need to reallocate the internal buffer. This has a time complexity of O(N+M), where N is the original buffer size and M is the new buffer size. Where possible, you should use the reserve method on vector to pre-allocate the internal buffer to minimise the requirement to reallocate the internal buffer. Although vector will grow dynamically it is most optimal to use when you know in advance how big the buffer needs to be.

在添加向量的所有情况下,时间复杂度都没有考虑到向量可能还需要重新分配内部缓冲区这一事实。 这具有O(N + M)的时间复杂度,其中N是原始缓冲区大小,M是新缓冲区大小。 在可能的情况下,应使用vector的reserve方法预先分配内部缓冲区,以最大程度地减少重新分配内部缓冲区的需求。 尽管向量将动态增长,但最好在事先知道缓冲区需要多大的情况下使用。

The vector is random access, meaning you can read any element of a vector in constant time, or O(1). This makes vector ideal for implementing constructs that require low latency reads.

向量是随机访问的,这意味着您可以在恒定时间内或O(1)读取向量的任何元素。 这使得vector非常适合于实现需要低等待时间读取的构造。

Consider using a vector if:

如果满足以下条件,请考虑使用向量:

you need to store data when you have a rough idea, in advance, of the number of items

事先对项目数量有一个大概的了解时,您需要存储数据

data can be either added all in one go or can be appended to the existing data

数据可以一次性添加,也可以附加到现有数据中

if you want to be able to access the contents in any order

如果您希望能够以任何顺序访问内容

Avoid using a vector if:

在以下情况下,避免使用向量:

you need to do frequent inserts or deletes to anywhere other then the back of the vector

您需要对向量的背面进行频繁的插入或删除操作

you do not know, in advance, roughly how much data you plan to put in it

您事先不知道您计划放入多少数据

A simple example of using vector
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
	std::vector<int> c;
	c.reserve(10);

	for(int x = 0 ; x < 10 ; ++x)
	{
		c.push_back(x);
	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

双端队列 (deque)

The deque is a sequence container that represents an abstraction of a dynamic one dimensional array. It manages an internal buffer which can automatically grow to accommodate the contents. The default allocation strategy used to allocate the internal buffer is not defined by the C++ Standard. Unless a user defined allocator is provided it will default to using a vendor specific allocator, which will provide a general allocation strategy for best case general use.

双端队列是一个序列容器,表示动态一维数组的抽象。 它管理一个内部缓冲区,该缓冲区可以自动增长以容纳内容。 C ++标准未定义用于分配内部缓冲区的默认分配策略。 除非提供了用户定义的分配器,否则它将默认使用供应商特定的分配器,该分配器将为一般情况下的最佳使用情况提供通用分配策略。

The size of the buffer may possibly be greater than the size of the data held within the deque. This is because the capacity of the deque (over actual size of the internal buffer) will always be at least as big as the size (the amount of the capacity in use) but may be greater.

缓冲区的大小可能会大于双端队列中保存的数据的大小。 这是因为双端队列的容量(超过内部缓冲区的实际大小)将始终至少与该大小(所使用的容量大小)一样大,但可能更大。

The actual internal implementation of the deque buffer is completely implementation dependent so we cannot draw any assumptions about how costly it is to grow the buffer; however, it is reasonable to assume that the worse case is going to be linear, of more specifically O(N) where N represents the size of the new buffer.

双端队列缓冲区的实际内部实现完全取决于实现,因此我们无法就增长缓冲区的成本做出任何假设; 但是,可以合理地假设最坏的情况是线性的,尤其是O(N),其中N表示新缓冲区的大小。

Unlike vector, deque provides no mechanism to reserve a buffer. However, this isn't actually such a big deal since deque doesn't have to comply with the contiguous memory requirements of vector (which it needs to remain backwards compatible with C arrays). Since this is the case compiler vendors are able to implement memory allocation strategies that are far more Operating System friendly. Or, to put it another way, it does not suffer from the same reallocation bottleneck of vector.

与向量不同,双端队列不提供保留缓冲区的机制。 但是,这实际上并不是什么大问题,因为双端队列不必遵守向量的连续内存要求(它必须保持与C数组的向后兼容性)。 由于是这种情况,编译器供应商能够实现对操作系统更友好的内存分配策略。 或者,换句话说,它不会遭受向量相同的重新分配瓶颈。

The internal buffer of a deque may not shrink when the container is cleared. This can be a problem if you have released a large quantity of data and want to reclaim the memory being used. Like vector the trick is to swap the deque with an empty one and then destroy the original one that was empty

清空容器后,双端队列的内部缓冲区可能不会缩小。 如果您释放了大量数据并想回收正在使用的内存,这可能是个问题。 像vector一样,技巧是将双端队列与一个空双端队列交换,然后销毁原来的一个

The deque gets its name from a contraction of "double ended queue" and is most efficient when appending items to the front or back (vector is only efficient when appending to the back).  If you think of the deque's internal buffer line of cups it’s easy to add new cup at either end. What if we want to insert a cup in the middle? Well, now it’s a little more complex. We have to move some of the cups to the left or the right to make space to insert a new one. It’s the same with a deque. If we want to insert a new item every item after it must move backwards or forwards to make space. Likewise, deleting anywhere other than the back or front of the deque is equally inefficient.

双端队列的名称来自“双头队列”的收缩,在将项目附加到正面或背面时效率最高(矢量仅在附加到背面时有效)。 如果您想到双端队列的杯子的内部缓冲线,则很容易在两端添加新杯子。 如果我们想在中间插入杯子怎么办? 好吧,现在有点复杂了。 我们必须将一些杯子向左或向右移动,以腾出空间来插入新杯子。 与双端队列相同。 如果我们要插入一个新项目,则每个项目必须向后或向前移动以腾出空间。 同样,删除双端队列的后方或前方以外的任何地方同样效率低下。

When appending to or removing from the back and front of a deque the time complexity is O(1). In other words, constant time as long as the buffer does not need to be reallocated.

当在双端队列的前后附加或从中删除时,时间复杂度为O(1)。 换句话说,只要不需要重新分配缓冲区就可以保持恒定的时间。

If the number of elements being inserted into or removed from a deque is know beforehand the time complexity is O(N+M) otherwise the time complexity is O(NM), where N is the number of items being inserted or removed and M is the distance or number of items that need to be moved.

如果预先知道要插入到双端队列中或从双端队列中删除的元素的数量,则时间复杂度为O(N + M),否则时间复杂度为O(NM),其中N是要插入或删除的项目数,M是需要移动的物品的距离或数量。

In all cases of adding to a deque, the time complexities do not take into account the fact that the deque may also need to reallocate the internal buffer.

在添加到双端队列的所有情况下,时间复杂度并未考虑到双端队列可能还需要重新分配内部缓冲区这一事实。

The deque is random access, meaning you can read any value of a deque in constant time, or O(1). This makes deque ideal for implementing constructs that require lowlatency reads.

双端队列是随机访问,这意味着您可以在恒定时间内读取双端队列的任何值或O(1)。 这使得双端队列非常适合于实现需要低延迟读取的构造。

Consider using a deque if:

如果出现以下情况,请考虑使用双端队列:

you need to store data and are likely to can be appended to the existing data at either end

您需要存储数据,并且很可能可以在任一端附加到现有数据上

you want to be able to access the contents in any order

您希望能够以任何顺序访问内容

Avoid using a deque if:

在以下情况下,请避免使用双端队列:

you need to do frequent inserts or deletes to anywhere other then the back or front of the deque

您需要对双端队列的背面或正面以外的其他位置进行频繁的插入或删除操作

you need to have compatibility with C style arrays (use vector)

您需要与C样式数组兼容(使用向量)

A simple example of using deque
#include <deque>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
	std::deque<int> c;

	for(int x = 0 ; x < 10 ; ++x)
	{
		if(0 == (x%2))
		{
			c.push_back(x);
		}
		else
		{
			c.push_front(x);
		}

	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

清单 (list)

The list is a sequence container that represents an abstraction of a doubly linked list. The list consist of nodes, which are allocated on a need too basis. The memory foot print of an empty list is very small; however, the memory footprint of each node is actually quite large and can, for example exceed the size of the data being stored in the node.

列表是一个序列容器,表示抽象链表的抽象。 该列表由节点组成,这些节点也是根据需要分配的。 空列表的内存占用很小。 但是,每个节点的内存占用量实际上很大,并且可能会例如超过该节点中存储的数据大小。

For example, if we store 32 bit int in the node on a 32 bit platform the overhead for each node will probably be at least 64 bits (the next and prev pointer, three times as much as the size of the data)! This means that list is, generally, a poor choice if you are looking to store small data items especially if you plan to store lots of them. Put another way, if you were storing 1 GB of 32 bit int values in the list it would have a memory footprint of at least 3GB.

例如,如果我们在32位平台上的节点中存储32位int,则每个节点的开销可能至少为64位(next和prev指针,是数据大小的三倍)! 这意味着,如果您要存储小数据项,特别是如果您计划存储很多数据项,那么通常,列表是一个差的选择。 换句话说,如果您在列表中存储1 GB的32位int值,则其内存占用量至少为3GB。

Although the cost of a node, in terms of memory, is quite high the list makes up for this by virtual of the fact that memory is only allocated when a node is created and can be released when the node is destroyed.  This means that it’s a good candidate for storing an unpredictable number of items, especially if the number of items is likely to fluctuate.

尽管就内存而言,节点的成本相当高,但该列表实际上是通过以下事实来弥补这一点的:仅在创建节点时才分配内存,而在销毁节点时才可以释放内存。 这意味着它是存储不可预测的项目数量的理想选择,尤其是在项目数量可能波动的情况下。

The downside is that unless the allocator used takes care to avoid it, rapid allocation and de-allocation of nodes in a list can lead to bad memory fragmentation.

不利的一面是,除非使用分配器注意避免这种情况,否则列表中节点的快速分配和取消分配会导致不良的内存碎片

Most STL implementations try to get around this by allocating a memory in chunks and using this to allocate the nodes. When a node is released the memory it used in the chunk becomes free. This is not a C++ Standards requirement so you should check your compilers documentation to see if this behaviour is supported.

大多数STL实现都尝试通过分块分配内存并使用它来分配节点来解决此问题。 释放节点后,该块中使用的内存将释放。 这不是C ++标准的要求,因此您应该查看编译器文档以查看是否支持此行为。

Since a list is, essentially, a sequential chain of nodes where one node points to the next and that in-turn points back it follows that if a node is remove or inserted into the list it will not invalidate the rest. This means that it is perfectly safe to iterate through a list whilst it is being modified; only the iterator to the modified node become invalid in the case of a node being removed.

因为从本质上说,列表是节点的顺序链,其中一个节点指向下一个节点,然后依次指向后方,因此,如果将一个节点删除或插入列表中,它将不会使其余节点失效。 这意味着在修改列表时,通过列表进行迭代是绝对安全的; 在删除节点的情况下,只有修改节点的迭代器无效。

It also follows that since a list is just a sequential chain of nodes access to them is also sequential and not direct as in the case of, for example, the items in a vector. In fact, access to a node within a list takes liner time, or more specifically it has a time complexity of O(N) where N is the distance from the node being accessed from the point we started from. This makes list unsuitable for certain algorithms such as a binary search.

还可以得出结论,由于列表只是节点的顺序链,因此对它们的访问也是顺序的,而不像例如向量中的项目那样是直接的。 实际上,访问列表中的节点要花费线性时间,或更具体地说,它的时间复杂度为O(N),其中N是从我们开始的点到要访问的节点的距离。 这使得列表不适用于某些算法,例如二进制搜索。

On the other hand, inserting, moving (even between type identical lists) or removing a node requires nothing more than the modification of the next and previous pointers in the three nodes affected (the previous, the current and the next) and so takes constant time, or more specifically it has a time complexity of O(1).

另一方面,插入,移动(甚至在类型相同的列表之间)或删除节点只需要修改受影响的三个节点(上一个,当前和下一个)中的下一个和上一个指针,因此需要常量时间,或更具体地说,它的时间复杂度为O(1)。

Consider using a list if:

如果满足以下条件,请考虑使用列表:

you need to store many items but the amount cannot be predicted

您需要存储很多物品,但数量无法预测

you need to perform lots of inserts or deletes that are not at the start or end of the sequence of data

您需要执行许多不在数据序列开头或结尾的插入或删除操作

Avoid using a list if:

避免使用列表,如果:

the size of your data items is small, especially if you need to store many of them

数据项的大小很小,尤其是当您需要存储许多数据项时

you need constant time random access to the items

您需要恒定时间随机访问物品

A simple example of using list
#include <list>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
	std::list<int> c;

	for(int x = 0 ; x < 10 ; ++x)
	{
		if(0 == (x%2))
		{
			c.push_back(x);
		}
		else
		{
			c.push_front(x);
		}

	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

(set)

The set is a sorted associative container that represents an abstraction of an ordered unique key. Although the exact implementation of the data structures that make up a set are not specifically defined in the C++ Standard they are typically implemented as a Binary Search Tree (more specifically, a common implementation is a Red/Black tree).

集合是一个排序的关联容器,代表有序唯一键的抽象。 尽管组成一个集合的数据结构的确切实现未在C ++标准中明确定义,但它们通常被实现为二进制搜索树 (更具体地说,常见的实现是红色/黑色树 )。

Just like list, set is a node based container meaning iterators are not invalidated when items are added or removed with the exception of the iterator of the specific item being removed.

就像list一样,set是一个基于节点的容器,这意味着在添加或删除项目时,迭代器不会无效,但要删除的特定项目的迭代器除外。

The keys in a set must be unique, attempting to add the same key more than once just replaces the existing item with the end result being nothing more than wasted time.

集合中的密钥必须唯一,尝试多次添加同一密钥,只是替换现有项,最终结果不过是浪费时间。

Accessing, adding and removing items in a set can be done directly via the key value although, unlike vector and deque, the access does not take place in constant time. In fact time taken is generally logarithmic, or more specifically it has a time complexity of O(log N), where N is the distance from the first node to the target node.

可以直接通过键值访问,添加和删除集合中的项目,尽管与vector和deque不同,访问不是在固定时间内进行的。 实际上,所花费的时间通常是对数的,或更具体地说,它的时间复杂度为O(log N),其中N是从第一个节点到目标节点的距离。

Just like list, one needs to consider the possible size overhead of the framework that makes up a node versus the size of the data being stored. If the size of the data is small, say the size of a 32 bit int on a 32 bit platform it is likely that the size of the node will be at last 3 times that of the original data item.

就像列表一样,需要考虑组成节点的框架可能的大小开销与所存储数据的大小。 如果数据的大小很小,例如在32位平台上的32位int的大小,则该节点的大小很可能将是原始数据项的大小的最后3倍。

Since set is a sorted associative container based on a Binary Search Tree it follows that keys are stored in a predetermined order. By default the order of the keys is determined by the std::less comparison predicate. You can override this behaviour by providing your own comparison predicate as one of the template parameters.

由于set是基于二叉搜索树的分类关联容器,因此可以按预定顺序存储键。 默认情况下,键的顺序由std :: less比较谓词确定。 您可以通过提供自己的比较谓词作为模板参数之一来覆盖此行为。

As an alternative to set you might want to consider Google's sparse_hash_set and dense_hash_set. Both of these are unsorted associate containers that are implemented using has tables rather than Binary Search Trees. They have several advantages over set, the mains ones are constant time lookup (best case, worse case can be linear!) and much lower cost in terms of data to framework overhead for each data item stored. Unlike set, the data is not sorted making hash table a poor choice if you need to access data in sorted order.

作为设置的替代方法,您可能需要考虑Google的sparse_hash_setdensity_hash_set 。 这两个都是未排序的关联容器,它们是使用has表而不是Binary Search Trees实现的。 它们比集合具有一些优点,主要优点是恒定时间查找(最好的情况,最坏的情况可以是线性的!),并且就存储的每个数据项而言,数据到框架的开销方面的成本要低得多。 与set不同,如果您需要按排序顺序访问数据,则不会对数据进行排序,因此哈希表不是一个好的选择。

Consider using a set if:

如果满足以下条件,请考虑使用集合:

you need to store data items in a sorted order and you need this sorting to be enforced in real time

您需要按排序顺序存储数据项,并且需要实时执行此排序

you need to filter out duplicates from a collection and wish to retain that list for further use

您需要从集合中过滤出重复项,并希望保留该列表以备将来使用

you cannot use a 3rd party hash set

您不能使用第三方哈希集

the nature of your data means it doesn't hash well, resulting in many

数据的性质意味着它不能很好地散列,导致许多

collisions 碰撞

Avoid using a set if:

在以下情况下,请避免使用集合:

the size of your data items is very small especially if you need to store lots of them

数据项的大小非常小,特别是如果您需要存储很多数据项时

you cannot afford to accept the fact that access is not done in constant time

您无法接受无法在固定时间内完成访问的事实

you can get away with using a hash set

您可以使用哈希集摆脱困境

A simple example of using set
#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>

int main()
{
	std::set<int> c;

	srand((int)time(0));

	for(int x = 0 ; x < 10 ; ++x)
	{
		c.insert(rand());
	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

地图 (map)

The map is a sorted associative container that represents an abstraction of an ordered unique key, with a related value. Although the exact implementation of the data structures that make up a map are not specifically defined in the C++ Standard they are typically implemented as a Binary Search Tree (|more specifically, a common implementation is a Red/Black tree).

映射是一个排序的关联容器,表示具有相关值的有序唯一键的抽象。 尽管组成映射的数据结构的确切实现未在C ++标准中明确定义,但它们通常被实现为Binary Search Tree(|更具体地说,常见的实现是Red / Black树)。

Just like list, map is a node based container meaning iterators are not invalidated when items are added or removed with the exception of the iterator of the specific item being removed.

就像列表一样,map是基于节点的容器,这意味着在添加或删除项目时,迭代器不会无效,但要删除的特定项目的迭代器除外。

The keys  in a map must be unique (although the related values do not have the same restriction), attempting to add the same key more than once just replaces the existing item and its value. Of course, if the new key has a different value then this will modify the value of the key in the map.

映射中的键必须是唯一的(尽管相关值没有相同的限制),尝试多次添加同一键只是替换现有项及其值。 当然,如果新键具有不同的值,则这将修改映射中键的值。

Accessing, adding and removing items in a map can be done directly via the key value although, unlike vector, the access does not take place in constant time. In fact time taken is generally logarithmic, or more specifically it has a time complexity of O(log N), where N is the distance from the first node to the target node.

可以直接通过键值来访问,添加和删除地图中的项目,尽管与向量不同,访问并不是在恒定的时间内进行的。 实际上,所花费的时间通常是对数的,或更具体地说,它的时间复杂度为O(log N),其中N是从第一个节点到目标节点的距离。

Just like list, one needs to consider the possible size overhead of the framework that makes up a node versus the size of the data being stored. If the size of the data is small, say the key size size of a 32 bit int and, likewise the value, on a 32 bit platform it is likely that the size of the node will be at last 2 times that of the original data item.

就像列表一样,需要考虑组成节点的框架可能的大小开销与所存储数据的大小。 如果数据的大小很小,则说一个32位int的密钥大小,以及同样的值,在32位平台上,该节点的大小很可能是原始数据的2倍。项目。

Since map is a sorted  associative container based on a Binary Search Tree it follows that keys are stored in a predetermined order. By default the order of the keys is determined by the std::less comparison predicate. You can override this behaviour by providing your own comparison predicate as one of the template parameters.

由于map是基于二叉搜索树的分类关联容器,因此可以按预定顺序存储键。 默认情况下,键的顺序由std :: less比较谓词确定。 您可以通过提供自己的比较谓词作为模板参数之一来覆盖此行为。

As an alternative to map you might want to consider Google's sparse_hash_map and dense_hash_map . Both of these are unsorted associate containers that are implemented using has tables rather than Binary Search Trees. They have several advantages over map, the mains ones are constant time lookup (best case, worse case can be linear!) and much lower cost in terms of data to framework overhead for each data item stored. Unlike map, the data is not sorted making hash table a poor choice if you need to access data in sorted order.

作为映射的替代方法,您可能需要考虑Google的sparse_hash_mapdensity_hash_map 。 这两个都是未排序的关联容器,它们是使用has表而不是Binary Search Trees实现的。 它们比map有几个优点,主要优点是恒定时间查找(最好的情况,最坏的情况是线性的!),并且对于每个存储的数据项而言,从数据到框架的开销而言,其成本要低得多。 与map不同,如果您需要按排序顺序访问数据,则不会对数据进行排序,因此哈希表不是一个好的选择。

Consider using a map if:

如果出现以下情况,请考虑使用地图:

you need to store data items in a sorted order of the key and you need this sorting to be enforced in real time

您需要按键的排序顺序存储数据项,并且需要实时实施这种排序

you need to filter out duplicates from a list and wish to retain that list for further use

您需要从列表中过滤出重复项,并希望保留该列表以备将来使用

you cannot use a 3rd party hash map

您不能使用第三方哈希图

the nature of your data means it doesn't hash well, resulting in many collisions

数据的性质意味着它不能很好地散列,从而导致许多冲突

Avoid using a map if:

如果发生以下情况,请避免使用地图:

the size of your data items is very small especially if you need to store lots of them

数据项的大小非常小,特别是如果您需要存储很多数据项时

you cannot afford to accept the fact that access is not done in constant time

您无法接受无法在固定时间内完成访问的事实

you can get away with using a hash set

您可以使用哈希集摆脱困境

A simple example of using map
#include <map>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>

namespace std
{
	ostream & operator << (ostream & os, pair<int, int> const & val)
	{
		return (os << val.first << "=>" << val.second);
	}
}

int main()
{
	std::map<int, int> c;

	srand((int)time(0));

	for(int x = 0 ; x < 10 ; ++x)
	{
		c[rand()] = x;
	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<std::pair<int, int> >(std::cout, "\n"));
}

多集 (multiset)

The multiset is a sorted associative container that represents an abstraction of an ordered non-unique key. The only difference between set and multiset is that a multiset is allowed to have duplicate keys.

多重集是一个排序的关联容器,表示有序非唯一键的抽象。 集合和多重集之间的唯一区别是,允许​​多重集具有重复的键。

A simple example of using multiset
#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>

int main()
{
	std::multiset<int> c;

	srand((int)time(0));

	for(int x = 0 ; x < 10 ; ++x)
	{
		c.insert(rand());
	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, "\n"));
}

多图 (multimap)

The multimap is a sorted associative container that represents an abstraction of an ordered non-unique key. The only difference between map and multimap is that a multimap is allowed to have duplicate keys.

多重映射是一个排序的关联容器,表示有序非唯一键的抽象。 映射和多图之间的唯一区别是,允许​​多图具有重复的键。

A simple example of using multimap
#include <map>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>

namespace std
{
	ostream & operator << (ostream & os, pair<int, int> const & val)
	{
		return (os << val.first << "=>" << val.second);
	}
}

int main()
{
	std::multimap<int, int> c;

	srand((int)time(0));

	for(int x = 0 ; x < 10 ; ++x)
	{
		c.insert(std::pair<int, int>(rand(), x));
	}

	std::copy(c.begin(), c.end(), std::ostream_iterator<std::pair<int, int> >(std::cout, "\n"));
}

位集 (bitset)

The  bitset isn't really a container since it doesn't expose any iterators; however, I list it here for completeness. The bitset is basically a fixed size bitfield, where the size is defined at compile time as a template parameter. It is useful if you need a bitfield that is of a size that doesn't match a standard intrinsic integer. It's also useful as it can automatically convert a string of an appropriate format to a bitfield value and vice-verca.

位集是不是一个真正的容器,因为它不公开任何迭代器; 但是,出于完整性考虑,我在这里列出。 该位集基本上是一个固定大小的位域,其中的大小在编译时定义为模板参数。 如果您需要的位域大小与标准内在整数不匹配,这将非常有用。 这也很有用,因为它可以将适当格式的字符串自动转换为位字段值,反之亦然。

A simple example of using bitset
#include <bitset>
#include <iostream>

int main()
{
	std::bitset<8> c(0xA7);
	std::cout << c.to_string() << std::endl;
}

That concludes our guided tour of the basic STL containers. Now you have a good grounding in STL containers you might want to read An Introduction to STL Algorithms. This excellent article by, "w00te", goes into some depth about STL Algorithms and how they can be used with containers.

到此结束我们对基本STL容器的导览。 现在,您已经对STL容器有了很好的了解,您可能需要阅读STL算法简介 。 这篇名为“ w00te”的出色文章深入探讨了STL算法以及如何将其与容器一起使用。

If you wish to find out more about STL containers please refer to the follow excellent guide.

如果您想了解有关STL容器的更多信息,请参考以下优秀指南

翻译自: https://www.experts-exchange.com/articles/2812/Which-STL-Container.html

stl 容器套容器 内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值