views:

809

answers:

2

One would think the simple code

llist1.Last.Next = llist2.First;
llist2.First.Previous = llist1.Last;

would work, however apparently in C#'s LinkedList, First, Last, and their properties are Get only.

The other method I could think of was

llist1.AddLast(llist2.First);

However, this does not work either - it fails because the first node of llist2 is already in a linked list.

Does this mean that I have to have a loop that manually AddLast's each node of llist2 to llist1? Doesn't this defeat the efficiency of linked lists????

+2  A: 
llist1 = new LinkedList<T>(llist1.Concat(llist2));

this concatenates the two lists (requires .NET 3.5). The drawback is that it creates a new instance of LinkedList, which may not be what you want... You could do something like that instead :

foreach(var item in llist2)
{
    llist1.AddLast(item);
}
Thomas Levesque
does this do the right thing for linked lists? or does it use the default iterate-over-everything method?
Jimmy
It does the iterate-over-everything method.
Reed Copsey
See my updated answer to avoid iterating over llist1 (you still have to iterate over llist2 though...)
Thomas Levesque
Concat puts two IEnumerables together in a lazy way. So if the resulting list is never traversed, this operation takes O(1). If they are traversed, there is no asymptotic overhead in traversing the first list, and then the second list. So Concat is a very attractive solution for reading the combined lists. It falls short if structural modifications to the combined list are desired, as there are still two distinct backing lists underneath, and structural modifications cannot be done through IEnumerables
Michael Donohue
+5  A: 

Yes, you have to loop, unfortunately. This is an O(n) operation - O(1) for each entry added. There's no risk of requiring a buffer to be resized and copied, etc - although of course garbage collection might do roughly that :) You could even write handy extension methods:

public static class LinkedListExtensions   
{
    public static void AppendRange<T>(this LinkedList<T> source,
                                      IEnumerable<T> items)
    {
        foreach (T item in items)
        {
            source.AddLast(item);
        }
    }

    public static void PrependRange<T>(this LinkedList<T> source,
                                       IEnumerable<T> items)
    {
        LinkedListNode<T> first = source.First;
        foreach (T item in items)
        {
            source.AddBefore(first, item);
        }
    }
}

EDIT: Erich's comment suggests why you might think this is inefficient - why not just join the two lists together by updating the "next" pointer of the tail of the first list and the "prev" pointer of the head of the second? Well, think about what would happen to the second list... it would have changed as well.

Not only that, but what would happen to the ownership of those nodes? Each is essentially part of two lists now... but the LinkedListNode<T>.List property can only talk about one of them.

While I can see why you might want to do this in some cases, the way that the .NET LinkedList<T> type has been built basically prohibits it. I think this doc comment explains it best:

The LinkedList<T>) class does not support chaining, splitting, cycles, or other features that can leave the list in an inconsistent state.

Jon Skeet
I think he means the efficiency of doing one operation (manipulate the "pointers" to append one list to the other) versus iterating over all the entries of either one. O(1) vs O(n) for the append operation.
Erich Mirabal
Manipulating the pointers directly would break the second list though.
Jon Skeet
Can you clarify what you mean when you say iteration and appending are O(1)? It doesn't sound right to me. I think appending one item is O(1), but iterating over n items is O(n).
Don Kirkby
It's the "of each entry" that's O(1). It's O(n) over the whole lot, yes. Will edit to make that clearer.
Jon Skeet
I know, I'm just saying that is what he meant. It doesn't seem that far out there to be able to do that (merge two lists), but the current .NET implementation makes it impossible with LinkedList (as you point out).
Erich Mirabal
Appending is O(1) since it is a doubly-linked list with "pointers" to the first and last item.
Erich Mirabal
+1. Now there's an answer that even Jon Skeet would be proud of... err.. umm.. you know what I mean :)
Erich Mirabal
Thanks for elaborating on the question - much appreciated. Must get back to that static one later on... blog post to write first though. Can't see the book going much further tonight...
Jon Skeet
Excellent answer. It's also worth noting that not only would both lists change, but they would become inextricably linked. Adding or removing a node from one could potentially affect the other - not something you would normally expect from seemingly independent list instances.
LBushkin