-3

I was trying to do some google work but could not find any relevant answer.

I want to get data from a database to a linked list. The syntactically easiest way I was able to discover was using LINQ:

var linkedListData = myLinqToSqlQuery.ToList() as LinkedList<T>;

However, I'm not sure what happens here internally (and I admit that I was lazy to figure out my answer from the IL).

What will be the internal structure coming out of this conversion? It pretends to be linked list, however, I would expect the conversion only changes the type, but not the internal structure. Thus internally the data should be stored in an array rather than a linked list.

Could anyone provide an answer or point me to a good explanation in the documentation or a blog post?


EDIT

Based on comments and answers I have realised that I have a bit overcomplicated my question. It could be as simple as:

What happens internally during explicit conversion between List and LinkedList in C#?

Does it call LinkedList construtor? Does it call some conversion operator (which one)?

List<T> list = new List<T>{ items };
var linkedList = list as LinkedList<T>;
Vočko
  • 2,478
  • 1
  • 23
  • 31
  • Either way, this **fails to compile**! *CS0039 C# Cannot convert type `System.Collections.Generic.List` to `System.Collections.Generic.LinkedList` via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion* – CoolBots Aug 26 '20 at 05:14
  • `IQueryable.ToList() as LinkedList`, compiles just fine. – Vočko Aug 26 '20 at 05:19
  • @Vočko would you mind sharing a minimal code example where this line of code compiles? It doesn't compile for me, and here's an attempt on SharpLab: https://sharplab.io/#v2:C4LgTgrgdgPgAgJgIwFgBQcAMACOSAsA3OlrkgHQAyAllAI7FokIDM2A3utt7gghz0HYuQ7gEkAihACmYAJ4BDAEYAbaQB5awAHzYAzgHsAttKmzFq6Y1HcAbgrDYw0vRBXBsAXn3HTM+cpq5AAqBjR6wAAUAJTYCnrYNFAA1tIAJuHAmlA6MSLcAL7oRWhAA=== – CoolBots Aug 26 '20 at 05:24
  • Well, you gotta get an IQueryable object. My example is a bit too complex to post it here, but generally you need something like `dbContext.Items.Where(i => i.Id > 0).Tolist() as LinkedList` where `T` is a type of `Item` – Vočko Aug 26 '20 at 05:29
  • Out of curiosity, what's wrong with `new LinkedList(myLinqToSqlQuery)` – CoolBots Aug 26 '20 at 05:31
  • My example on SharpLab has an `IQueryable` object. **It doesn't compile!** It doesn't matter where `List` is coming from, there is no conversion to `LinkedList` . What .NET version is this? Do you have some package installed that provides a conversion between `List` and `LinkedList`? I just tried this on .NET Core 3.1 with EF Core 3.1.7 - no go. – CoolBots Aug 26 '20 at 05:32
  • @CoolBots The fact that LinkedList doesn't have any constructor taking IQueryable? – Vočko Aug 26 '20 at 05:35
  • @Vočko yes it does - `IQueryable` is also `IEnumerable`: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1.-ctor?view=netcore-3.1#System_Collections_Generic_LinkedList_1__ctor_System_Collections_Generic_IEnumerable__0__ – CoolBots Aug 26 '20 at 05:37
  • @CoolBots You are right, sorry, I wanted to avoid it as I have to do conversion `new LinkedList((IQueryable)IQueryable).Where(...)`. but it actually might be a better option. However, still doesn't answer my original question about the internal representation after conversion. – Vočko Aug 26 '20 at 05:42
  • Interesting, so you have an `IQueryable`, not an `IQueryable`? What is the fully-qualified type name of `myLinqToSqlQuery`? – CoolBots Aug 26 '20 at 05:46
  • @CoolBots I get the object from a database and you can't get generic types so I get `IQueryable` but as the rest of the class is generic I want to return `IQueryable`. But this discussion is off topic to my question. – Vočko Aug 26 '20 at 05:52
  • Your updated code *definitely* doesn't compile - `as` is invalid between `List` and `LinkedList` - try it in a fresh Console .NET Core project, to make sure you don't have some magical package installed – CoolBots Aug 26 '20 at 07:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/220489/discussion-between-vocko-and-coolbots). – Vočko Aug 26 '20 at 11:25

1 Answers1

1

Edit: Since the OP made edits to the question and the followup questions raised by others I have edited my answer.

To start with, List to LinkedList does not have any implicit or explicit conversion.

However, the following demo code compiles. While this is not exactly the same as the OP's stated question, I have broken down the steps.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Entity;

namespace linkedlist
{
    class Program
    {
        static void Main(string[] args)
        {
            var arr = new [] { "first", "second", "third" };
            var q1 = from x in arr
                     select x;
            var q = q1.ToList();
            var ll = new LinkedList<string>(q);

        }
    }
}

Why not work with the List directly, unless you are performing a lot of operations for which the LinkedList is optimal compared to List (See this SO query and answers on List vs LinkedList)?

The last statement creates a new LinkedList<T> which copies the contents of the List<T>.

If the intent is to obtain a LinkedList anyway from a LINQ query, the following is less wasteful:

    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var arr = new [] { "first", "second", "third" };
        var q1 = from x in arr
                 select x;
        var ll = new LinkedList<string>(q1);

    }

Experiments

Here is some data on performance of Linq to List<T> to LinkedList<T> vs. Linq to LinkedList<T>

Experiment code

using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Entity;

namespace linkedlist
{
    class Program
    {
        static void Main(string[] args)
        {
            var arr = new[] { "first", "second", "third" };
            var q1 = from x in arr
                     select x;

            for(var j = 0; j < 10; j++)
            {
                var st1 = DateTime.Now;
                for (var i = 0; i < 10000; i++)
                {
                    var q = q1.ToList();
                    var ll = new LinkedList<string>(q);
                }
                var en1 = DateTime.Now;
                Console.WriteLine($"With LINQ to List<T> to LinkedList<T> {(en1 - st1).TotalMilliseconds}");

                var st2 = DateTime.Now;
                for (var i = 0; i < 10000; i++)
                {
                    var ll = new LinkedList<string>(q1);
                }
                var en2 = DateTime.Now;
                Console.WriteLine($"With LinQ to LinkedList<T> {(en2 - st2).TotalMilliseconds}");
            }
        }
    }
}

Timing Results:

Screenshot:

enter image description here

The first run should be ignored to discount cache warm-up effect as we are using the same LINQ query for both tests. All times are Total Elapsed Milliseconds for 10,000 iterations per trial and a total of 10 trials.

With LINQ to List<T> to LinkedList<T> 20.0769
With LinQ to LinkedList<T> 8.6571

With LINQ to List<T> to LinkedList<T> 12.0162
With LinQ to LinkedList<T> 8.7212
With LINQ to List<T> to LinkedList<T> 12.7354
With LinQ to LinkedList<T> 8.3412
With LINQ to List<T> to LinkedList<T> 12.6848
With LinQ to LinkedList<T> 8.1306
With LINQ to List<T> to LinkedList<T> 12.3656
With LinQ to LinkedList<T> 8.5935
With LINQ to List<T> to LinkedList<T> 19.8802
With LinQ to LinkedList<T> 7.9596
With LINQ to List<T> to LinkedList<T> 12.1177
With LinQ to LinkedList<T> 9.0604
With LINQ to List<T> to LinkedList<T> 18.219
With LinQ to LinkedList<T> 11.115
With LINQ to List<T> to LinkedList<T> 18.4309
With LinQ to LinkedList<T> 11.2717
With LINQ to List<T> to LinkedList<T> 18.6743
With LinQ to LinkedList<T> 11.4202

Conclusion:

Linq to LinkedList is faster than Linq to List to LinkedList and less wasteful.

vvg
  • 1,010
  • 7
  • 25
  • I will use the LinkedList as a priority queue with a lot of AddFirst and AddAfter and thus the LinkedList should perform much better. Can you explain/source the information about conversion changing the internal structure as it was my original question. – Vočko Aug 26 '20 at 05:50
  • @Vočko you should consider a Heap data structure if you need a priority queue. LinkedList makes a terrible priority queue. – CoolBots Aug 26 '20 at 05:52
  • You are wrong @CoolBots. It is less wasteful compared to the code segment that does Query to List to LinkedList. Query to List would materialize the list into memory. List to LinkedList will copy. Query to LinkedList will do only one materialization pass. – vvg Aug 26 '20 at 15:51
  • You are missing the point. In the code block that contains var q = q1.ToList(); there will be an extra List fully in memory. If you have 100k items, it occupies memory. In the last code block in my answer, we don't have this List materialization. We have var ll = new LinkedList(q1); which will do the foreach enumeration directly on the LINQ query. That's why I say it is less wasteful. In the former, there aer two copies in memory at the end of the processing, not counting LINQ internal memory. In the latter, there is only one copy, again not counting LINQ internal memory. – vvg Aug 26 '20 at 16:06
  • @CoolBots -- did it. With just 3 items in the Linq result set, the difference is staggering. See for yourself. Experiment code and result data published above. Thanks. – vvg Aug 26 '20 at 17:29
  • Ok, I am wrong - I was pretty sure the `foreach` has to enumerate, but that doesn't appear to be the case; I retracted my downvote. I'll dig into the code to really understand it. I will retract my comments. Btw, use `System.Diagnostics.Stopwatch` instead of `DateTime`, it's a more typical method for timing diagnostics data. – CoolBots Aug 26 '20 at 18:00
  • 1
    foreach does enumerate. The reason for the perf difference is q = q1.ToList() does a foreach enumeration and the new LinkedList(q) constructor does another foreach enumeration. Whereas if we go from Linq query to LinkedList() constructor directly, there is only one foreach enumeration. – vvg Aug 26 '20 at 18:10
  • Hmm... I think we're both wrong... I agree that it appears to be faster to not call `ToList()`, but the *memory usage* suggests there is no extra space being taken up - I just tried on a *billion* items. This article also supports my understanding of how `foreach` works: https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/april/essential-net-understanding-csharp-foreach-internals-and-custom-iterators-with-yield ...but again, you're right that the extra `ToList` call has some cost to it. It's not quite double, neither in time (visible from your own experiment), nor in memory usage... – CoolBots Aug 26 '20 at 18:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/220519/discussion-between-vvgiri-and-coolbots). – vvg Aug 26 '20 at 18:36