16

I got a simple List of ints.

List<int> myInts = new List<int>();

myInts.Add(0);
myInts.Add(1);
myInts.Add(4);
myInts.Add(6);
myInts.Add(24);

My goal is to get the first unused (available) value from the List.

(the first positive value that's not already present in the collection)

In this case, the answer would be 2.

Here's my current code :

int GetFirstFreeInt()
{
    for (int i = 0; i < int.MaxValue; ++i)
    {
        if(!myInts.Contains(i))
            return i;
    }

    throw new InvalidOperationException("All integers are already used.");
}

Is there a better way? Maybe using LINQ? How would you do this?

Of course here I used ints for simplicity but my question could apply to any type.

2 Answers2

27

You basically want the first element from the sequence 0..int.MaxValue that is not contained in myInts:

int? firstAvailable = Enumerable.Range(0, int.MaxValue)
                                .Except(myInts)
                                .FirstOrDefault();

Edit in response to comment:

There is no performance penalty here to iterate up to int.MaxValue. What Linq is going to to internally is create a hashtable for myInts and then begin iterating over the sequence created by Enumerable.Range() - once the first item not contained in the hashtable is found that integer is yielded by the Except() method and returned by FirstOrDefault() - after which the iteration stops. This means the overall effort is O(n) for creating the hashtable and then worst case O(n) for iterating over the sequence where n is the number of integers in myInts.

For more on Except() see i.e. Jon Skeet's EduLinq series: Reimplementing LINQ to Objects: Part 17 - Except

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • Nice, but what if myInts contains {0,1,2,3} ? I would need the method to return 4 in that case. –  Jan 14 '12 at 22:23
  • @asmo: Ah that wasn't clear from the question - in that case use int.MaxValue as upper bound not `myInts.Max()` - I will update the answer. – BrokenGlass Jan 14 '12 at 22:24
  • @asmo Make it `myInts.Max() + 1` – Adi Lester Jan 14 '12 at 22:24
  • 2
    @Lester: Then you have the edge condition if myInts.Max() is already int.MaxValue – BrokenGlass Jan 14 '12 at 22:25
  • @BrokenGlass True, but I think it's worth adding a check for that specific case in order to avoid (possibly needlessly) enumerating 2 billion integers. – Adi Lester Jan 14 '12 at 22:27
  • 2
    @Lester: see updated answer - there is no performance penalty since the outer sequence is lazily enumerated and only until the first unused integer is found. – BrokenGlass Jan 14 '12 at 22:39
  • 1
    Is there a reason to use a nullable int? Won't the default value be 0, not null? – Forss Dec 07 '15 at 13:52
2

Well, if the list is ordered from smallest to largest and contains values from 0 to positive infinity, you could simply access the i-th element. if (myInts[i] != i) return i; which would be essentially the same, but doesn't necessitate iterating through the list for each and every Contains check (the Contains method iterates through the list, turning your algorithm into an O(n-squared) rather than O(n)).

GGulati
  • 1,027
  • 6
  • 11
  • +1. Yes, and if the list is unsorted it is faster to sort it before doing GGulati's test. Sorting is O(n*log(n)), which is faster than O(n^2) for the iterated Contains check. (If I am right, Microsoft is using QuickSort.) – Olivier Jacot-Descombes Jan 14 '12 at 22:31
  • Sorting won't be faster than using a HashSet which is O(n) for OPs problem – BrokenGlass Jan 14 '12 at 22:49