270

Say I have 3 strings in a List (e.g. "1","2","3").

Then I want to reorder them to place "2" in position 1 (e.g. "2","1","3").

I am using this code (setting indexToMoveTo to 1):

listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, itemToMove);

This seems to work, but I am occasionally getting strange results; sometimes the order is incorrect or items from the list are getting deleted!

Any ideas? Does List<T> guarantee order?

Related:

Does a List<T> guarantee that items will be returned in the order they were added?

Community
  • 1
  • 1
SuperSuperDev1234
  • 4,765
  • 5
  • 26
  • 19

5 Answers5

363

The List<> class does guarantee ordering - things will be retained in the list in the order you add them, including duplicates, unless you explicitly sort the list.

According to MSDN:

...List "Represents a strongly typed list of objects that can be accessed by index."

The index values must remain reliable for this to be accurate. Therefore the order is guaranteed.

You might be getting odd results from your code if you're moving the item later in the list, as your Remove() will move all of the other items down one place before the call to Insert().

Can you boil your code down to something small enough to post?

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Bevan
  • 43,618
  • 10
  • 81
  • 133
  • 69
    For any future googler's here is the exact quote from the MSDN (bolding mine) for [List(T).Add](http://msdn.microsoft.com/en-us/library/3wcytfd1.aspx) _The object to be added to the **end** of the List. The value can be null for reference types._ – aolszowka May 05 '11 at 22:29
  • 5
    Is there a more definitive quote/reference we could get from Microsoft or the C# specification about this? @aolszowka's quote definitely seems to suggest that it does retain insertion order, but technically the List could re-order the collection any time after an item was added and that statement would still be valid. I don't want to get nit-picky about it, but if a manager or QA really made me defend this position, I wouldn't feel very confident with just that quote. – tehDorf Jul 17 '15 at 16:41
  • 4
    I can think of two useful ways to obtain some confirmation. First, [read the source](http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646) and satisfy yourself. Secondly, check out the definition of the abstract data type `List` in any good computer science textbook. Just like `Queue` and `Stack`, a `List` is a well defined, predictable and well understood data structure - if the .NET implementation differed (or if it changes) a *lot* of software would break. – Bevan Jul 18 '15 at 06:12
  • @tehDorf My take on it (if there were to be a discussion on it) is: "If ordering affects the operation of your method/algorithm you should explicitly order the list or have your API take the appropriate Interface/class such as IOrderedEnumerable or SortedList, otherwise you should not rely on a particular implementation to behave a certain way unless it is explicitly stated unambiguously" You also have to be careful about explicitly sorting List as their implementation uses an unstable sort. – aolszowka Jul 18 '15 at 16:30
  • @Beavan Sorry for the confusion. While I agree that in computer science a `List` retains its order, I was just pointing out that I didn't see where on MSDN they actually stated that they are following that. However, your comment prompted me to compare MSDN's description of `List` to its description of `Stack` and `Queue` and I figured out what I was missing... – tehDorf Jul 18 '15 at 16:52
  • ...`List` "Represents a strongly typed list of objects that can be **accessed by index**." Before I had assumed that just meant you could use `[x]` to access the x'th element of the list (but it wasn't guaranteed not to change), but I understand now that means that "accessing index `[x]` will always return object `y` (unless you change the ordering by inserting/removing/sorting). – tehDorf Jul 18 '15 at 16:52
  • @Bevan, will the List guarantee the order of insertion even if we use "where"? Eg. I added a new employee to my list. I filter "emp.where x=> x.empDept = 'abc').LastorDefault()" --> Will this bring me the newly "added" employee only? – superachu Aug 01 '16 at 19:25
  • 1
    @achuthakrishnan They are separate issues. List guarantees that items are retained in the order they are added to the list. When you use the LINQ `Where()` method to filter the list, you're relying on the implementation to consider the items in the sequence in order. It happens that it does (see http://referencesource.microsoft.com/System.Core/System/Linq/Enumerable.cs.html#e73922753675387a), but this is not documented in MSDN. I'd suggest using `emp.LastOrDefault(x => x.empDept = 'abc')` as the documentation of `LastOrDefault()` does indicate it maintains the order of items. – Bevan Aug 01 '16 at 22:22
  • Hi. You know if the behaviour is the same with the inline initialization? – Gioce90 May 18 '17 at 09:23
  • Inline initialization is just syntactic sugar for adding things manually, so yes, the behaviour is the same. – Bevan May 19 '17 at 04:08
  • This, as written, is utter nonsense. You can always insert or remove. So, no the index is not fixed. It'll not change by itself, of course. – TaW Sep 13 '18 at 21:32
41

Here are 4 items, with their index

0  1  2  3
K  C  A  E

You want to move K to between A and E -- you might think position 3. You have be careful about your indexing here, because after the remove, all the indexes get updated.

So you remove item 0 first, leaving

0  1  2
C  A  E

Then you insert at 3

0  1  2  3
C  A  E  K

To get the correct result, you should have used index 2. To make things consistent, you will need to send to (indexToMoveTo-1) if indexToMoveTo > indexToMove, e.g.

bool moveUp = (listInstance.IndexOf(itemToMoveTo) > indexToMove);
listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, moveUp ? (itemToMoveTo - 1) : itemToMoveTo);

This may be related to your problem. Note my code is untested!

EDIT: Alternatively, you could Sort with a custom comparer (IComparer) if that's applicable to your situation.

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
Joel Goodwin
  • 5,026
  • 27
  • 30
  • Didn't read Bevan's response carefully enough, I've just covered the ground he has. – Joel Goodwin Jun 25 '09 at 10:34
  • 9
    Yeah, but you've elaborated and given an example of Bevan's answer, as well as some solution code, so your answer is not tautologous and I've voted you up. – Jason S Nov 16 '11 at 20:36
9

As Bevan said, but keep in mind, that the list-index is 0-based. If you want to move an element to the front of the list, you have to insert it at index 0 (not 1 as shown in your example).

M4N
  • 94,805
  • 45
  • 217
  • 260
1

This is the code I have for moving an item down one place in a list:

if (this.folderImages.SelectedIndex > -1 && this.folderImages.SelectedIndex < this.folderImages.Items.Count - 1)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index + 1, imageName);
    this.folderImages.SelectedIndex = index + 1;
 }

and this for moving it one place up:

if (this.folderImages.SelectedIndex > 0)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index - 1, imageName);
    this.folderImages.SelectedIndex = index - 1;
}

folderImages is a ListBox of course so the list is a ListBox.ObjectCollection, not a List<T>, but it does inherit from IList so it should behave the same. Does this help?

Of course the former only works if the selected item is not the last item in the list and the latter if the selected item is not the first item.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
1

If you will change the order of operations, you will avoid the strange behavior: First insert the value to the right place in the list, and then delete it from his first position. Make sure you delete it by his index, because if you will delete it by reference, you might delete them both...

Asaf
  • 477
  • 6
  • 6