Click to See Complete Forum and Search --> : Change Item in a LinkedList


FranciscoDias
January 7th, 2010, 06:29 PM
I have a list and I want to change one item for another


public void ActualizarProduto(Produto obj)
{
foreach (Produto tmp in ListProdutos)
{
if (obj.Text == tmp.Text)
{
tmp = obj;
return;
}
}
}


it seems I can't use tmp = obj because it is a foreach;
(btw: it will only find one result - there is only on item with same text)

If you have any ideia, it would be very helpful :) thank you in advance

BigEd781
January 7th, 2010, 06:53 PM
You cannot change objects in a linked list because you will destroy the current state of the iterator. If you need to do that, you can use LINQ, a regular for loop (careful here), or make a temporary list of objects to change and then do the swapping after the foreach loop.

MadHatter
January 7th, 2010, 11:05 PM
You cannot change objects in a linked list inside a foreach loop because you will destroy the current state of the iterator...

forgot a part.

TheGreatCthulhu
January 9th, 2010, 09:50 AM
Ok. Assuming that your Produto is a reference type (a class, not a struct), and that ListProdutos is List<Produto>, here’s why it doesn’t work.

The tmp variable obtains a reference to the object in the list. With tmp, you can alter the state of the referenced object (the one in the list), or call some of it’s methods.

Now, when
tmp = obj
is executed, tmp references a NEW object, and has no connection to the one in the list. You just change the REFERENCE.

Suppose it’s the first object in the list:



[tmp]---(ref: L1)---------> [L1] [L2][L3][L4]



This turns to:



[tmp]---(ref: obj)--------->[obj]

[L1][L2][L3][L4]



You can use the for loop and the indexer to access the object IN THE LIST directly, and then change it.


for (int i = 0; i < ListProdutos.Count; i++)
{
if (obj.Text == ListProdutos[i].Text)
{
ListProdutos[i] = obj;
return;
}
}

TheGreatCthulhu
January 9th, 2010, 10:01 AM
Here's a simple example:




public class MyClass
{
public int value = 0;

public MyClass(int val)
{
value = val;
}
}

class Program
{
static void Main(string[] args)
{
List<MyClass> list = new List<MyClass>();

for (int i = 0; i < 5; i++)
list.Add(new MyClass(i));

MyClass obj = new MyClass(-12); // just a random number I picked...

MyClass tmpRef = list[3];

list[2] = obj; // affects the list.
tmpRef = obj; // the list is not affected!

for (int i = 0; i < list.Count; i++)
Console.WriteLine(list[i].value.ToString());
}
}



And the output:

0
1
-12
3
4
Press any key to continue . . .

MadHatter
January 9th, 2010, 02:03 PM
foreach uses the iterator pattern. behind the scenes it's implemented as a while(iEnumerator.MoveNext()) internally it keeps a version number which is changed when you add or remove items in the list, and when you remove an item it throws an exception because it's being iterated and the version changes (which it wont allow).

TheGreatCthulhu
January 9th, 2010, 03:30 PM
Oh, your code won’t compile. Slipped that detail of my mind…
But, my point is – bear in mind that you’re working with object references.

Anyway, the Iterator pattern enables you to access elements of a collection, without the need to worry about the way the collection is implemented. You can treat arrays, lists, hash tables, or any implementation of IEnumerable the same way. The way it works is that any IEnumerable object creates it’s version (implementation) of IEnumerator on request, and the client code (you, via foreach) just uses the IEnumerator interface.

Now, the compiler won’t allow you to change the variable declared in the foreach loop’s “header”, since it has “read-only context” (MSDN help on the error), and even if it did, you wouldn’t change the underlying list. And it’s a good thing it won’t let you do it. The compiler prevented you to create a weird bug.
I’m just not sure what exactly the foreach block turns into after the compilation, but as the others have said – it would probably make the IEnumerator instance invalid.

The foreach looks nicer, but why not use the for loop? Unless there is a good reason for all that indirection/flexibility? If you are concerned about the design and reusability, if at some point in the future you decide to change the type of the underlying collection, then this might become a problem (but maybe it won’t – only you can tell).

TheGreatCthulhu
January 10th, 2010, 06:19 AM
I only now noticed the title. You’re using a LinkedList…
I didn’t pay attention, sorry. :cry:
Here’s the same example as before, except it now uses a LinkedList.


static void Main(string[] args)
{
LinkedList<MyClass> list = new LinkedList<MyClass>();

for (int i = 0; i < 5; i++)
list.AddLast(new MyClass(i));

MyClass obj = new MyClass(-12); // just a random number I picked...

LinkedListNode<MyClass> node = list.First;
for (int i = 0; i < list.Count; i++)
{
if (i == 2)
{
// If you just make the (current) node reference something else,
// you'll create a list that's in an inconsistent state
// (since Previous & Next will be null)!

// This effectively replaces the current node.
list.AddAfter(node, obj);
LinkedListNode<MyClass> addedNode = node.Next;
list.Remove(node);

node = addedNode; // this makes the WriteLine() work
}

Console.WriteLine(node.Value.value.ToString());
node = node.Next;
}
}



Output:

0
1
-12
3
4
Press any key to continue . . .

I hope it helps.