Skip to content

Instantly share code, notes, and snippets.

@kshyju
Last active May 15, 2021 19:25
Show Gist options
  • Save kshyju/d1903a06b84263de4a458f7046247dab to your computer and use it in GitHub Desktop.
Save kshyju/d1903a06b84263de4a458f7046247dab to your computer and use it in GitHub Desktop.
Blog-2021_05_15_LINQIntersectAny
IEnumerable<string> first = new[] {"one", "two", "three","four","five","six","seven","eight"};
IEnumerable<string> second = new[] { "two", "three"};
var matchFound = first.Intersect(second, StringComparer.OrdinalIgnoreCase).Any();
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.22362
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.203
  [Host]     : .NET Core 3.1.15 (CoreCLR 4.700.21.21202, CoreFX 4.700.21.21402), X64 RyuJIT
  DefaultJob : .NET Core 3.1.15 (CoreCLR 4.700.21.21202, CoreFX 4.700.21.21402), X64 RyuJIT

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
Arrays_Linq_Intersect_Any 397.45 ns 7.843 ns 11.739 ns 0.0958 - - 752 B
Arrays_Custom_IntersectAny 52.67 ns 1.071 ns 1.570 ns - - - -
List_Linq_Intersect_Any 450.73 ns 8.313 ns 9.896 ns 0.0978 - - 768 B
List_Custom_IntersectAny 99.32 ns 1.963 ns 2.016 ns 0.0153 - - 120 B
[MemoryDiagnoser]
public class IntersectAnyBenchmarks
{
ICollection<string> first = new [] { "ten" };
ICollection<string> second = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight" };
List<string> list1 = new List<string> { "ten" };
List<string> list2 = new List<string> { "one", "two", "three", "four", "five", "six", "seven", "eight" };
[Benchmark]
public bool Arrays_Linq_Intersect_Any()
{
return first.Intersect(second).Any();
}
[Benchmark]
public bool Arrays_Custom_IntersectAny()
{
return first.IntersectAny(second);
}
[Benchmark]
public bool List_Linq_Intersect_Any()
{
return list1.Intersect(list2).Any();
}
[Benchmark]
public bool List_Custom_IntersectAny()
{
return list1.IntersectAny(list2);
}
}
public static class CollectionExtensions
{
/// <summary>
/// Returns the collection as an array.
/// If the collection is already an array, it will be returned,
/// else creates a new array by copying the contents of the collection, which is an O(n) operation.
/// </summary>
/// <param name="source">The collection to return as a list</param>
/// <typeparam name="T">The type of element in the collection.</typeparam>
/// <returns>Array populated from the source collection or null.</returns>
public static T[]? AsArray<T>(this ICollection<T>? source)
{
if (source == null)
{
return null;
}
if (source is T[] sourceConvertedAsArray)
{
return sourceConvertedAsArray;
}
T[] returnArray = new T[source.Count];
source.CopyTo(returnArray, 0);
return returnArray;
}
/// <summary>
/// Determines there is a common element present in the two collections passed in by doing a case insensitive check.
/// This is a fast, less allocation alternative to calling "Any" on the result of "Intersect".
/// Returns false, If any of the collections parameter passed in is null.
/// </summary>
/// <param name="first">First collection.</param>
/// <param name="second">Second collection.</param>
/// <returns>True if a common element is present, else False.</returns>
public static bool IntersectAny(this ICollection<string>? first, ICollection<string>? second)
{
// Working with a for loop on an array is faster than working with
// an Enumerator(foreach). So we will first convert the ICollection to an array.
// If the parameters are already an array, this method allocates zero bytes.
string[]? firstArray = first.AsArray();
string[]? secondArray = second.AsArray();
if (firstArray == null || secondArray == null)
{
return false;
}
for (var i = 0; i < firstArray.Length; i++)
{
for (var j = 0; j < secondArray.Length; j++)
{
if (firstArray[i].Equals(secondArray[j], StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment