[C#.Net] FirstOrDefault or SingleOrDefault -2

[C#/.Net] FirstOrDefault or SingleOrDefault?

Introduction

As developers, haven't we all wondered whether to use a FirstOrDefault or a SingleOrDefault?

If you're a young developer, you've probably dismissed this question out of hand, telling yourself that in the vast majority of cases, good old FirstOrDefault does the job, because "who can do more, can do less".

On the other hand, some Developers, through an automatism acquired during their training or through personal conviction, use SingleOrDefault when the element to be identified is actually supposed to be unique.

But what is it really?

A little semantics

FirstOrDefault

Linq method which :

Returns the first element of a sequence, or a default value if the sequence contains no elements.

Source: Microsoft Learn

In short, this method allows you to browse a collection and extract the first element corresponding to the specified predicate (or the first entry if no predicate is specified), or a default value if no match with the predicate is identified.

SingleOrDefault

Linq method which :

Returns a specific single element of a sequence, or a default value if the element cannot be found.

Source: Microsoft Learn

In short, this method allows you to browse a collection and extract the single element corresponding to the specified predicate, or a default value if no match with the predicate is identified.

In practice, what's the difference?

General operation

Theoretically, SingleOrDefault would be preferable in cases where it's certain that the predicate being searched for is unique in the collection being browsed.

To ensure this, the method will scan the entire collection, even if the predicate is identified during processing.

On the other hand, the FirstOrDefault stops its action as soon as it identifies the search object.

So what happens if there is more than one match in the collection?

In the case of SingleOrDefault, it's very simple: an Exception is thrown because this situation is not supposed to occur.

As far as FirstOrDefault is concerned, it doesn't matter if there are several matches: as soon as it encounters one, it returns it.

A picture worth a thousand words.

FirstOrDefault :

FirstOrDefault

SingleOrDefault :

It's essential to bear in mind that there is a risk associated with the use of SingleOrDefault, depending on the elements present in the source collection. So it's best to understand its use and plan for it in your code to ensure robustness.

"Yes, but Jamy, if the Single is proposed it's because it should be more efficient/optimized when used under the intended conditions, right?"

In reality, it's not quite that simple...

Perfomances

As mentioned above, in essence, SingleOrDefault scans the entire collection before returning a result... Or an Exception.

FirstOrDefault doesn't bother.

What does this mean in terms of processing times and memory usage?
Is this negligible, or should it be taken into account during development?

Let's take a look at the Benchmark*:

Protocol

  • We're going to create collections of different sizes (100, 10,000, 1,000,000) => Haystacks.
  • For each collection, we'll insert a single element, which will be the one targeted by our predicate => The needle.
  • For each haystack, the needle is inserted at different positions (first, middle, last).
  • We'll then measure the time and memory used to extract the needle from the various hay bales using FirstOrDefault and SingleOrDefault.

Results

Development
  • Position: 0 (first entry in collection), 0.5 (middle), 1 (last entry in collection)
  • Size: Collection size
  • Mean: Treatment duration
  • Allocated: Memory allocated for processing

Comments

As we can see from the table, in almost all cases, FirstOrDefault wins out in terms of execution times.

The processing time is roughly the same when the element to be identified is the last in the collection. This makes sense, as it's the only case in which the FirstOrDefault has to go through the entire collection.

In fact, as the size of the collection increases, so does the difference in processing time between the two methods.

With regard to memory consumption, we can see that the two processes are similar, except when the size of the collection is very large (1,000,000 in our test). In this case, SingleOrDefault consumes more memory than its counterpart.

Conclusion

In conclusion, the penalty is quite clear-cut. Apart from one specific case, FirstOrDefault is totally advantageous in terms of execution time and resource savings.

On the other hand, the risk of SingleOrDefault's Exception being raised seems to make its "rival" the default choice.

 

However, if any of you have had experiences or situations that suggest the use of SingleOrDefault rather than FirstOrDefault, don't hesitate to share them with us.

It's also important to bear in mind that .Net Core is constantly evolving. Linq queries, in particular, have undergone major changes over the last few versions. It is therefore possible that some of these points will need to be updated in the future.

If you have any questions on this subject, please do not hesitate to contact me.

(*): BenchmarkDotNet is a .Net library (Nuget package) dedicated to performance measurement.

Image by Michaël LESTAVEL

Michaël LESTAVEL

IS Consultant (specialized in .Net)

Share this article

Share this article

Contents

Read also

Read the article
Read the article
Read the article