在C#中生成下一个可用的唯一名称

时间:2009-04-08 18:38:49

标签: c# .net performance

如果您的应用中有一个命名系统,其中应用包含100个操作,这会创建新对象,例如:

Blur
Sharpen
Contrast
Darken
Matte
...

每次使用其中一个时,都会创建一个具有唯一可修改名称的新实例,例如Blur01Blur02Blur03Sharpen01,{{ 1}}等等。如何生成下一个可用的唯一名称,以便它是O(1)操作或接近恒定时间。请记住,用户还可以将名称更改为自定义名称,例如Matte01等。

有一些限制可以接受,例如使用字母,数字,下划线等将字符数限制为100 ......

编辑:您也可以在没有“填补空白”的情况下建议解决方案,但不会重复使用已经使用但已删除的名称,当然除了自定义名称。

8 个答案:

答案 0 :(得分:7)

我会在动作类中创建一个静态整数,它会增加并作为每个新类实例的一部分进行分配。例如:

class Blur
{
    private static int count = 0;

    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Blur()
    {
        _name = "Blur" + count++.ToString();
    }
}

由于count是静态的,因此每次创建新类时,它都会递增并附加到默认名称。 O(1)时间。

修改

如果您在删除时需要填写漏洞,我建议如下。它会在重命名项目时自动排队,但整体成本会更高:

class Blur
    {
        private static int count = 0;
        private static Queue<int> deletions = new Queue<int>();

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                Delete();
            }
        }

        private int assigned;

        public Blur()
        {
            if (deletions.Count > 0)
            {
                assigned = deletions.Dequeue();
            }
            else
            {
                assigned = count++;
            }
            _name = "Blur" + assigned.ToString();
        }

        public void Delete()
        {
            if (assigned >= 0)
            {
                deletions.Enqueue(assigned);
                assigned = -1;
            }
        }
    }

此外,删除对象时,您需要在对象上调用.Delete()。

CounterClass字典版

class CounterClass
{
   private int count;
   private Queue<int> deletions;

   public CounterClass()
   {
      count = 0;
      deletions = new Queue<int>();
   }

   public string GetNumber()
   {
      if (deletions.Count > 0)
      {
          return deletions.Dequeue().ToString();
      }
      return count++.ToString();
   }

   public void Delete(int num)
   {
      deletions.Enqueue(num);
   }
}

您可以创建一个Dictionary来查找每个字符串的计数器。只要确保解析索引并在重命名或删除值时调用.Delete(int)。

答案 1 :(得分:7)

我推荐你到Michael A. Jackson的Two Rules of Program Optimization

  1. 不要这样做。
  2. 仅限专家:不要这样做。
  3. 简单,可维护的代码比优化您认为可能以后的速度问题更为重要。

    我会从简单开始:构建候选名称(例如“Sharpen01”),然后遍历现有过滤器以查看该名称是否存在。如果是,请递增并重试。这是O(N 2 ),但在您获得数千个过滤器之前,这就足够了。

    如果在某个时候,O(N 2 )确实成为一个问题,那么我首先要构建一个现有名称的HashSet。然后,您可以针对HashSet检查每个候选名称,而不是迭代。每次需要唯一名称时重建HashSet,然后扔掉;你不需要面对变化维护它的复杂性。这将使您的代码易于维护,而只是O(N)。

    O(N)就足够了。你不需要O(1)。用户不会单击“锐化”足够的时间以使其有任何差异。

答案 2 :(得分:3)

您可以在O(m)中轻松执行此操作,其中m是该名称的现有实例数(并且不依赖于n,即列表中的项目数。

  1. 查找有问题的字符串S。如果列表中没有S,您就完成了。
  2. S存在,因此请构建S+"01"并检查。继续递增(例如,下一次尝试S+"02",直到它不存在。
  3. 这为您提供了独特的名称,但它们仍然“漂亮”且易读。

    除非您预期会有大量重复项,否则这应该是“接近常量”的时间,因为m会非常小。

    警告:如果字符串自然地以结尾,例如,该怎么办? “01”?在你的情况下,这听起来不太可能,所以也许你不在乎。如果您关心,请考虑添加更多后缀,例如“_01”而不仅仅是“01”,因此更容易将它们分开。

答案 3 :(得分:2)

你可以这样做:

    private Dictionary<string, int> instanceCounts = new Dictionary<string, int>();

    private string GetNextName(string baseName)
    {
        int count = 1;

        if (instanceCounts.TryGetValue(baseName, out count))
        {
            // the thing already exists, so add one to it
            count++;
        }

        // update the dictionary with the new value
        instanceCounts[baseName] = count;

        // format the number as desired
        return baseName + count.ToString("00");
    }

然后您可以通过使用您想要的基本名称调用GetNextName(...)来使用它,例如 string myNextName = GetNextName("Blur");

使用它,您不必预先启动字典。 当你使用各种基本单词时,它会填写。 另外,这是O(1)。

答案 4 :(得分:1)

我会创建一个包含字符串键和整数值的字典,存储用于给定操作的下一个数字。这在实践中几乎是O(1)。

private IDictionary<String, Int32> NextFreeActionNumbers = null;       

private void InitializeNextFreeActionNumbers()
{
   this.NextFreeActionNumbers = new Dictionary<String, Int32>();

   this.NextFreeActionNumbers.Add("Blur", 1);
   this.NextFreeActionNumbers.Add("Sharpen", 1);
   this.NextFreeActionNumbers.Add("Contrast", 1);
   // ... and so on ...
}

private String GetNextActionName(String action)
{
   Int32 number = this.NextFreeActionNumbers[action];

   this.NextFreeActionNumbers[action] = number + 1;

   return String.Format("{0} {1}", action, number);
}

您必须检查与用户编辑的值的冲突。字典可能是一个明智的选择。没有办法解决这个问题。无论您如何生成名称,用户始终可以将现有名称更改为您生成的下一个名称,除非您将所有现有名称包含在生成模式中。 (或者使用用户编辑过的名称中不允许的特殊字符,但这样不太好。)

由于重复使用漏洞的评论,我也想在这里添加它。 不要重新使用重命名或删除的洞。这会使用户感到困惑,因为他删除或修改的名称会突然重新出现。

答案 5 :(得分:1)

我会寻找简化问题的方法。

是否有可以应用的约束?举个例子,如果每个用户只能有一个(活动)类型的动作,那会好吗?然后,可以使用用户的名称(或ID)来区分操作。

  • 模糊(Ben F)
  • Blur(Adrian H)
  • 焦点(Ben F)

在这种情况下,也许这不是一个选项,但也许其他东西是可能的。为了避免一些提议的解决方案的复杂性,我会竭尽全力!

答案 6 :(得分:0)

如果您想要O(1)时间,那么只需跟踪每个实例的数量。保留包含所有可能对象的哈希表,在创建对象时,递增该对象的值并在名称中使用结果。

答案 7 :(得分:0)

您肯定不想将GUID公开给用户界面。

您是在提议像“Blur04”这样的初始名称,让用户重命名,然后在用户的自定义名称冲突时引发错误消息?或者默默地将其重命名为“CustomName01”或其他什么?

您可以使用字典在O(1)时间内检查重复项。您可以在创建新效果实例的类中为每种效果类型设置递增计数器。就像凯文提到的那样,如果在删除效果时必须填写编号中的空白,情况会变得更加复杂。