如果您的应用中有一个命名系统,其中应用包含100个操作,这会创建新对象,例如:
Blur
Sharpen
Contrast
Darken
Matte
...
每次使用其中一个时,都会创建一个具有唯一可修改名称的新实例,例如Blur01
,Blur02
,Blur03
,Sharpen01
,{{ 1}}等等。如何生成下一个可用的唯一名称,以便它是O(1)操作或接近恒定时间。请记住,用户还可以将名称更改为自定义名称,例如Matte01
等。
有一些限制可以接受,例如使用字母,数字,下划线等将字符数限制为100 ......
编辑:您也可以在没有“填补空白”的情况下建议解决方案,但不会重复使用已经使用但已删除的名称,当然除了自定义名称。
答案 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:
简单,可维护的代码比优化您认为可能以后的速度问题更为重要。
我会从简单开始:构建候选名称(例如“Sharpen01”),然后遍历现有过滤器以查看该名称是否存在。如果是,请递增并重试。这是O(N 2 ),但在您获得数千个过滤器之前,这就足够了。
如果在某个时候,O(N 2 )确实成为一个问题,那么我首先要构建一个现有名称的HashSet。然后,您可以针对HashSet检查每个候选名称,而不是迭代。每次需要唯一名称时重建HashSet,然后扔掉;你不需要面对变化维护它的复杂性。这将使您的代码易于维护,而只是O(N)。
O(N)就足够了。你不需要O(1)。用户不会单击“锐化”足够的时间以使其有任何差异。
答案 2 :(得分:3)
您可以在O(m)
中轻松执行此操作,其中m
是该名称的现有实例数(并且不依赖于n
,即列表中的项目数。
S
。如果列表中没有S
,您就完成了。S
存在,因此请构建S+"01"
并检查。继续递增(例如,下一次尝试S+"02"
,直到它不存在。这为您提供了独特的名称,但它们仍然“漂亮”且易读。
除非您预期会有大量重复项,否则这应该是“接近常量”的时间,因为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)来区分操作。
在这种情况下,也许这不是一个选项,但也许其他东西是可能的。为了避免一些提议的解决方案的复杂性,我会竭尽全力!
答案 6 :(得分:0)
如果您想要O(1)时间,那么只需跟踪每个实例的数量。保留包含所有可能对象的哈希表,在创建对象时,递增该对象的值并在名称中使用结果。
答案 7 :(得分:0)
您肯定不想将GUID公开给用户界面。
您是在提议像“Blur04”这样的初始名称,让用户重命名,然后在用户的自定义名称冲突时引发错误消息?或者默默地将其重命名为“CustomName01”或其他什么?
您可以使用字典在O(1)时间内检查重复项。您可以在创建新效果实例的类中为每种效果类型设置递增计数器。就像凯文提到的那样,如果在删除效果时必须填写编号中的空白,情况会变得更加复杂。