为每个类的实例分配唯一ID

时间:2011-04-20 22:45:43

标签: c++ xml algorithm

我正在从XML文件导入项目。每个XML元素(FoodItemPersonOrderCoffeeRun)都是一个类&每个元素都有一个唯一的ID(该类是唯一的)。

<person>
<id>0</id>
<name>...</name>
</person>

<FoodItem>
<id>0</id>
<name>Coffee</name>
</FoodItem>

我正在尝试开发一个子类DatabaseItem,以确保一个类中没有2个对象具有相同的ID。你能帮助我开发一种有效的算法来帮助我,确保没有对象与另一个对象具有相同的ID吗?

我的两种方法对我来说似乎有点低效:

  • 到目前为止,使用包含所有USED ID的静态类向量。当创建一个新的DatabaseID( int requestedID )对象时,我通过遍历向量中的所有使用值来检查ID是否可用,以检查ID是否已经存在,我认为那是Big-O'n的速度?

  • 使用静态类bool vector,其中vector的每个元素对应一个id(因此vector[1]将对应ID为1的对象)。我通过查看vector中的元素是否为真if ( v[nID] == true ) { // this ID is already taken }来检查是否已经使用了ID。这似乎效率低下,因为这意味着我的vector会占用很多记录吗?

  • 我不熟悉在C ++中使用地图,但也许我应该在这里使用一个?

有关高效算法的任何建议都会非常有用:

class DatabaseItem
{
    public:
        static unsigned int instanceCount;

        DatabaseItem()
        {
            // Assign next available ID
        }

        DatabaseItem( unsigned int nID )
        {
            // Check that that id is not already taken
            // if id is taken, look for next available id &
            // give the item that id
        }

    private:
        unsigned int uniqueID;
};

// My solution: Do you have any better ideas that ensure no objects jave the same ID?
// This seems REALLY inefficient...
class DatabaseItem
{
    public:
        static unsigned int instanceCount;
        static vector <unsigned int> usedIDs;

        DatabaseItem()
        {
            DatabaseItem::instanceCount++;
            uniqueID = instanceCount;
            usedIDs.add( instanceCount );
        }

        DatabaseItem( unsigned int nID )
        {
            if ( isIDFree( nID ) )
            {
                uniqueID = nID;
            }
            else uniqueID = nextAvailableID();

            DatabaseItem::instanceCount++;
        }

        bool isIDFree( unsigned int nID )
        {
            // This is pretty slow to check EVERY element

            for (int i=0; i<usedIDs.size(); i++)
            {
                if (usedIDs[i] == nID)
                {
                    return false;
                }
            }

            return true;
        }

        unsigned int nextAvailableID()
        {
            while ( true )
            {
                unsigned int ID = 0;

                if ( isIDFree( ID ) )
                {
                    return ID;
                }
                else ID++;
            }
        }

    private:
        unsigned int uniqueID;
};


// Alternate that uses boolean vector to track which ids are occupied
// This means I take 30000 boolean memory when I may not need all that
class DatabaseItem
{
    public:
        static unsigned int instanceCount;
        static const unsigned int MAX_INSTANCES = 30000;
        static vector <bool> idVector;

        // Is this how I initialise a static class vector...? (note this code will be outside the class definition)
        // vector <bool> DatabaseItem::idVector( MAX_INSTANCES, false );

        DatabaseItem()
        {
            uniqueID           = nextAvailableID();
            idVector[uniqueID] = true;
        }

        DatabaseItem( unsigned int nID )
        {
            if ( nID >= MAX_INSTANCES )
            {
                // not sure how I shd handle this case?
            }

            if ( idVector[nID] == false )
            {
                uniqueID      = nID;
                idVector[nID] = true;
            }
            else
            {
                uniqueID           = nextAvailableID();
                idVector[uniqueID] = true;
            }

            instanceCount++;
        }

        unsigned int nextAvailableID()
        {
            for (int i=0; i<idVector.size(); i++)
            {
                if ( !idVector[i] )
                {
                    return i;
                }
            }

            return -1;
        }

        bool isIDFree( unsigned int nID )
        {
            // Note I cannot do this: Because I am using Mosync API & it doesn't support any C++ exceptions'

            // I declare idVector with no size! so not idVector( 30000, false)... just idVector;
            // then I allow an exception to occur to check if an id is taken

            try
            {
                return idVector[nID];
            }
            catch (...)
            {
                return true;
            }
        }

    private:
        unsigned int uniqueID;
};

3 个答案:

答案 0 :(得分:1)

每个bool实现一个vector<bool>,所以它不会浪费你所假设的空间。

set<unsigned int>是解决此问题的简单方法。 vector<bool>更快。两者都可以使用一点内存。根据您的使用模式,还有一些其他解决方案:

  • unsigned int all_taken_upto_this;set<int>相结合,涵盖高于all_taken_upto_this的所有古怪ID - 从集合中移除并在可能的情况下增加计数器。

  • A map<unsigned int, unsigned int>在逻辑上被视为已采用或自由序列的begin,end。这需要花一点时间才能正确实现(在两个元素之间添加最后一个ID时合并连续的地图元素,例如。)

您可能使用预制的“稀疏位集”类型数据结构 - 我不知道任何实现OTOH。

答案 1 :(得分:0)

根据元素的数量和其他几个问题,您可能会考虑在地图中实际存储它们(或至少指向它们)。这将很容易实现,但需要一些空间。另一方面,它将为您提供id的快速查找,如果XML中存在交叉引用,这可能是一个明显的优势。地图(假设指针)看起来像:

std::map<int, std::shared_ptr<Object> > id_map;
std::shared_ptr<Object> p( new Object( xml ) );
if ( !id_map.insert( std::make_pair( p->id, p ) ).second ) {
   // failed to insert, the element is a duplicate!!!
} 

答案 2 :(得分:0)

如果您没有使用整数,则可以查看GUID(全局唯一ID)。根据您使用的平台,您通常可以找到几个实用程序函数来动态生成GUID。如果使用Visual Studio,我使用了CoCreateGuid函数。

如果您被锁定为32位整数,则另一个选项选项是哈希表。如果每个XML元素都是唯一的,则散列函数可以生成唯一的散列值。根据数据集的大小,仍然存在很小的冲突概率。我使用的那个似乎与我使用的数据集具有相当低的冲突率称为Jenkins hash function