如何生成一个不在IEnumerable中的新ID?

时间:2016-08-11 11:26:25

标签: c# linq ienumerable

我有IEnumerable<int>,其中包含所有现有ID。我想生成一个新的id,它是现有ID中不存在的任何int。我有一个黑客攻击解决方案,但我想知道最好的方法来做到这一点。

// Inefficient solution
public static int GetFreshId(this IEnumerable<int> existingIds)
{
    int i = Int32.MinValue;
    while (existingIds.Contains(i)) // There is a case where all ids are taken!
    {
        i += 1;
    }
    return i;
}

更新:此处最佳定义为:

  • 满足要求
  • 可预测的表现
  • 最小的可能性
  • 应该适用于任何IEnumerable实施,对某些人来说,比其他人更快
  • 应该是无国籍的

5 个答案:

答案 0 :(得分:3)

如果您不在乎使用最低免费 ID,您可以直接使用当前最大ID的继承者:

import UIKit

extension Notification.Name
{
    enum MyNames
    {
        static let Hello = Notification.Name(rawValue: "HelloThere")
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.post(name: Notification.Name.MyNames.Hello ,object: nil, userInfo: nil)
    }
}

如果已经包含public static int GetFreshId(this IEnumerable<int> existingIds) { return existingIds.Max() + 1; } Int32.MaxValue,则会出现问题,因此您需要针对此案例进行一些特殊处理。

但是看到Int32.MinValue范围内存在多少个ID,这应该很少发生,因此可以为该角落案例实施更昂贵的算法。

如果您害怕溢出,可以先改进序列,然后扫描间隙(而不是测试每个可能的Int32 - 值),从而改进第一种方法:

int

编辑:感谢伊万打败了我的大错,相应改进了第二种方法

答案 1 :(得分:1)

您的解决方案的问题是这一行代码在循环中执行:

existingIds.Contains(i)

这具有O(N2)的复杂性。改进它的一种方法是使用与哈希而不是索引一起使用的集合。例如HashSet<T>

public static int GetFreshId(this IEnumerable<int> existingIds)
{
    var hashedIds = new HashSet<int>(existingIds);

    int i = Int32.MinValue;

    while (hashedIds.Contains(i)) ++i;    // now it use fast O(1) lookups

    return i;
}

答案 2 :(得分:1)

我只想添加一些异常处理。这不会在范围内丢失数字。

public static int GetFreshId(this IEnumerable<int> existingIds)
{
    if (existingIds == null) {
        throw new ArgumentNullException(nameof(existingIds));
    }
    if (!existingIds.Any()){
        return int.MinValue;
    }
    var lastId = existingIds.Max();
    if (lastId == Int.MaxValue){
        throw new ApplicationException("Sorry there are no more int available. Consider switching to int64.");
    }
    return lastId+1;
}

答案 3 :(得分:0)

如果你确定你永远不会有那么多你的Id等于Int32.MaxValue的项目,那么下面的内容会足够快

public static int GetFreshId(this IEnumerable<int> existingIds)
{
    return existingIds.Max() + 1;
}

答案 4 :(得分:0)

如果您的ID列表中没有任何空白

public static int GetFreshId(IEnumerable<int> existingIds) {    
    if (existingIds.Any()) {
        int i = existingIds.Max();  
        if (i == Int32.MaxValue) {
                throw new Exception("Ups...");
        }

        return i++;
    }

    return 1; // or what else
}

如果您可能认为您的解决方案没问题,可能只需添加检查以避免溢出