Collections类族主要包括三种形式及其不同应用:
Array — CArray, CObArray, CStringArray, CPtrArray, CByteArray, CWordArray, CDWordArray, CTypedPtrArray
List — CList, CObList, CStringList, CPtrList
CMap — CMap, CTypedPtrMap, CMapPtrToPtr, CMapPtrToWord, CMapWordToPtr, CMapStringToPtr
整体比较:
属性 类族 特点
内存结构 Array 在VC源码CArray::SetSize()中可看到:
TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];
// copy new data from old
memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));
// construct remaining elements
ASSERT(nNewSize > m_nSize);
ConstructElements<TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData;
m_pData = pNewData;
而在RemoveAt()中则有:
int nMoveCount = m_nSize - (nIndex + nCount);
DestructElements<TYPE>(&m_pData[nIndex], nCount);
if (nMoveCount)
memmove(&m_pData[nIndex], &m_pData[nIndex + nCount],
nMoveCount * sizeof(TYPE));
m_nSize -= nCount;
可见Array采用队列方式存储数据,因而其内部数据元素是以物理方式顺序排列的,所以检索、顺序执行GetAt()等函数的速度是相当快的。但是由于每次队列长度变化后,数据都要重新申请内存、拷贝内存、释放内存,因而Insert/Add/RemoveAt()的速度都很慢。
如果你使用的数据元素尺寸相当大,而且数组的操作相当复杂,频繁(1E4以上的)使用InsertAt/SetAt/RemoveAt等,应该考虑使用CList来代替。
一个特例是PtrArray/CTypedPtrArray,其内部数据是某个数据地址,而非数据本身,所以其效率更接近CList。
List 在VC源码CList::AddTail()中可看到:
CNode* pNewNode = NewNode(m_pNodeTail, NULL);
pNewNode->data = newElement;
if (m_pNodeTail != NULL)
m_pNodeTail->pNext = pNewNode;
因而List采用链表方式存储数据,因而当链表数据有所变动时,只做了一下指向变动,所以即使数据元素非常多单个数据元素也很大,执行Insert/Add/Remove的速度都很快,但是因为没有统一的Index,因而如果要找到某个元素只有遍历整个链表。
整体上说,List的使用比较繁琐,特别为小尺寸数据设计List更是得不偿失的,这也是为什么有CWordArray而没有CWordList的原因,因而在大多数情况下应该有限考虑是否可以使用Array来存储数据。
Map Map采用杂凑(Hashing)表方式来存储和检索数据,其内容包括Key和Value的双列表及其之间的映射关系表。例如在OODBLib用到的::FhtRegister <Class>()函数,在内部使用了Key=Class名称/Value=Class与数据库列的映射,从而在使用Class时只需要知道类的名字即可找到与之对应的数据库映射方法。
在下面的Characteristics表中可看到,Key的数值不可重复,即只能是唯一的(例如在上例中同一个程序中不可能有两个相同名字的类的声明),而Value可重复(不同名字的类可有完全相同的变量和函数)。
尽管杂凑表使用了高效的检索方式,大量使用CMap::LookUp()函数还是很耗时的,400 000次LookUp()执行时间大约是1秒,而同样次数的CObArray::GetAt()函数只需要0.3秒。
应用技巧 Array ①如果在你所使用的场合中,出现下列情况之一:a.数据元素的尺寸很大b.经常需要执行Add函数(例如数据库在执行Select语句时) 则需要事先执行SetSize(int nSize,int nGrowBy)函数,事先确定大致尺寸(之后仍可增加)及每次增加时一次申请的内存数,从而避免频繁执行内存申请、复制和删除工作。
②所有的命名中含Ptr的类都不进行删除内存的工作,需要自己完成。
③如果在循环中涉及RemoveAt()函数,循环一定要从尾部开始,否则可能漏掉元素,例:
CTypedPtrArray <CObArray,CPeople*> PeopleArray;
for (int i=PeopleArray.GetSize()-1;i>=0;i--)
{
if (PeopleArray.GetAt(i)->Name()=="Tom")
{
delete PeopleArray.GetAt(i);
PeopleArray.RemoveAt(i); //cy: 如果从i=0循环,则删除i后会漏掉对i-1的检查。
}
}
List (暂缺)
Map ①由于LookUp的执行速度比较慢,如果必须进行映射,可在第一此LookUp时将其地址获得并记录,此后可直接使用。参考OODBLib/COODBQuery::COODBQuery(),注意
m_cpOODBCDMap=::FhtOODBCDMapOf(aClass.CDMapName());
语句完成了上述操作。
下面是MSDN中Collections: Choosing a Collection Class章节的一些性能列表及注释。
Collection Shape Features
Shape Ordered? Indexed? Insert an element Search for specified element Duplicate elements?
List Yes No Fast Slow Yes
Array Yes By int Slow Slow Yes
Map No By key Fast Fast No (keys)
Yes (values)
Characteristics of MFC Collection Classes
Class Uses C++ templates Can be serialized Can be dumped Is type-safe
CArray Yes Yes 1 Yes 1 No
CByteArray No Yes Yes Yes 3
CDWordArray No Yes Yes Yes 3
CList Yes Yes 1 Yes 1 No
CMap Yes Yes 1 Yes 1 No
CMapPtrToPtr No No Yes No
CMapPtrToWord No No Yes No
CMapStringToOb No Yes Yes No
CMapStringToPtr No No Yes No
CMapStringToString No Yes Yes Yes 3
CMapWordToOb No Yes Yes No
CMapWordToPtr No No Yes No
CObArray No Yes Yes No
CObList No Yes Yes No
CPtrArray No No Yes No
CPtrList No No Yes No
CStringArray No Yes Yes Yes 3
CStringList No Yes Yes Yes 3
CTypedPtrArray Yes Depends 2 Yes Yes
CTypedPtrList Yes Depends 2 Yes Yes
CTypedPtrMap Yes Depends 2 Yes Yes
CUIntArray No No Yes Yes 3
CWordArray No Yes Yes Yes 3
1. To serialize, you must explicitly call the collection object’s Serialize function; to dump, you must explicitly call its Dump function. You cannot use the form ar << collObj to serialize or the form dmp << collObj to dump.
2. Serializability depends on the underlying collection type. For example, if a typed pointer array is based on CObArray, it is serializable; if based on CPtrArray, it is not serializable. In general, the “Ptr” classes cannot be serialized.
3. If marked Yes in this column, a nontemplate collection class is type-safe provided you use it as intended. For example, if you store bytes in a CByteArray, the array is type-safe. But if you use it to store characters, its type safety is less certain.