All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.plugins.csharp.S6617.html Maven / Gradle / Ivy

There is a newer version: 10.2.0.105762
Show newest version

Why is this an issue?

When testing if a collection contains a specific item by simple equality, both ICollection.Contains(T item) and IEnumerable.Any(x ⇒ x == item) can be used. However, Any searches the data structure in a linear manner using a foreach loop, whereas Contains is considerably faster in some collection types, because of the underlying implementation. More specifically:

  • HashSet<T> is a hashtable, and therefore has an O(1) lookup
  • SortedSet<T> is a red-black tree, and therefore has a O(logN) lookup
  • List<T> is a linear search, and therefore has an O(N) lookup, but the EqualityComparer is optimized for the T type, which is not the case for Any

For small collections, the performance difference may be negligible, but for large collections, it can be noticeable.

What is the potential impact?

We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from the More info tab.

Exceptions

Since LINQ to Entities relies a lot on System.Linq for query conversion, this rule won’t raise when used within LINQ to Entities syntaxes.

How to fix it

Contains is a method defined on the ICollection<T> interface and takes a T item argument. Any is an extension method defined on the IEnumerable<T> interface and takes a predicate argument. Therefore, calls with simple equality checks like Any(x ⇒ x == item) can be replaced by Contains(item).

This applies to the following collection types:

Code examples

Noncompliant code example

bool ValueExists(HashSet<int> data) =>
    data.Any(x => x == 42);
bool ValueExists(List<int> data) =>
    data.Any(x => x == 42);

Compliant solution

bool ValueExists(HashSet<int> data) =>
    data.Contains(42);
bool ValueExists(List<int> data) =>
    data.Contains(42);

Resources

Documentation

Articles & blog posts

Benchmarks

Method Runtime Mean Standard Deviation Allocated

HashSet_Any

.NET 7.0

35,388.333 us

620.1863 us

40132 B

HashSet_Contains

.NET 7.0

3.799 us

0.1489 us

-

List_Any

.NET 7.0

32,851.509 us

667.1658 us

40130 B

List_Contains

.NET 7.0

375.132 us

8.0764 us

-

HashSet_Any

.NET Framework 4.6.2

28,979.763 us

678.0093 us

40448 B

HashSet_Contains

.NET Framework 4.6.2

5.987 us

0.1090 us

-

List_Any

.NET Framework 4.6.2

25,830.221 us

487.2470 us

40448 B

List_Contains

.NET Framework 4.6.2

5,935.812 us

57.7569 us

-

Glossary

The results were generated by running the following snippet with BenchmarkDotNet:

[Params(10_000)]
public int SampleSize;

[Params(1_000)]
public int Iterations;

private static HashSet<int> hashSet;
private static List<int> list;

[GlobalSetup]
public void Setup()
{
    hashSet = new HashSet<int>(Enumerable.Range(0, SampleSize));
    list = Enumerable.Range(0, SampleSize).ToList();
}

[Benchmark]
public void HashSet_Any() =>
    CheckAny(hashSet, SampleSize / 2);

[Benchmark]
public void HashSet_Contains() =>
    CheckContains(hashSet, SampleSize / 2);

[Benchmark]
public void List_Any() =>
    CheckAny(list, SampleSize / 2);

[Benchmark]
public void List_Contains() =>
    CheckContains(list, SampleSize / 2);

void CheckAny(IEnumerable<int> values, int target)
{
    for (int i = 0; i < Iterations; i++)
    {
        _ = values.Any(x => x == target);  // Enumerable.Any
    }
}

void CheckContains(ICollection<int> values, int target)
{
    for (int i = 0; i < Iterations; i++)
    {
        _ = values.Contains(target); // ICollection<T>.Contains
    }
}

Hardware configuration:

BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.203
  [Host]               : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
  .NET 7.0             : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
  .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256




© 2015 - 2024 Weber Informatics LLC | Privacy Policy