What is the Most Efficient way to compare large List of integers to smaller List of integers?
algorithm, c#, performance
Solution
Three options now - the first two are more general, in that they don't rely on `MillionIntegerList` being sorted (which wasn't originally specified). The third is preferable in the case where the large list is already sorted.
Option 1
Yes, there's definitely a better way of doing it, using LINQ:
var common = MillionIntegerList.Intersect(TwoThousandIntegerList).ToList();
That will internally use a `HashSet<int>` built via the `TwoThousandIntegerList`, then look up each element of `MillionIntegerList` within it - which will be much more efficient than going through the whole of `TwoThousandIntegerList` each time.
If you only want the non-blacklisted ones, you need:
var valid = MillionIntegerList.Except(TwoThousandIntegerList).ToList();
Note that if you only need to iterate over the results once, you should remove the `ToList` call - I've included it to materialize the results so they can be examined multiple times cheaply. If you're just iterating, the return value of `Intersect` or `Except` will just stream the results, making it much cheaper in terms of memory usage.
Option 2
If you don't want to rely on the implementation details of LINQ to Objects but you still want a hash-based approach:
var hashSet = new HashSet<int>(TwoThousandIntegerList);
hashSet.IntersectWith(MillionIntegerList);
// Now use hashSet
Option 3
The approach of using the fact that the large list is sorted would definitely be useful.
Assuming you don't mind sorting the blacklisted list first as well, you could write a streaming (and general purpose) implementation like this (untested):
// Note: to use this, you'd need to make sure that *both* sequences are sorted.
// You could either sort TwoThousandIntegerList in place, or use LINQ's OrderBy
// method.
public IEnumerable<T> SortedIntersect<T>(this IEnumerable<T> first,
IEnumerable<T> second) where T : IComparable<T>
{
using (var firstIterator = first.GetEnumerator())
{
if (!firstIterator.MoveNext())
{
yield break;
}
using (var secondIterator = second.GetEnumerator())
{
if (!secondIterator.MoveNext())
{
yield break;
}
T firstValue = firstIterator.Current;
T secondValue = secondIterator.Current;
while (true)
{
int comparison = firstValue.CompareTo(secondValue);
if (comparison == 0) // firstValue == secondValue
{
yield return firstValue;
}
else if (comparison < 0) // firstValue < secondValue
{
if (!firstIterator.MoveNext())
{
yield break;
}
firstValue = firstIterator.Current;
}
else // firstValue > secondValue
{
if (!secondIterator.MoveNext())
{
yield break;
}
secondValue = secondIterator.Current;
}
}
}
}
}
(You could take an `IComparer<T>` if you wanted instead of relying on T being comparable.)
Problem
At the moment I have a `list` of 1million `integers`, and I check each `integer` against a blacklist of 2000 `integer`s. This is taking about 2 minutes. ``` for(int i = 0; i< MillionIntegerList.Length ; i++) { for(int blacklisted = 0; blacklisted < TwoThousandIntegerList.Length ; blacklisted++) if(i==blacklisted) i = 0; //Zero is a sentinel value } ``` This makes 2,000,000,000 iterations(loops) altogether. Is there a better way Im not seeing? thanks