1 dimensional List does return the same values on different elements.

Hi everyone,

I have some strange behavior of a 1 dimensional List array.
After adding some different values with ItemList[0].Add(…); Each Element have the same value.
Does anyone know what I’m doing wrong here?

Thank you!

// Declare SingleItem
public class SingleItem {
        public int ID { get; set; }
        public int Damage { get; set; }
    }

// Declare List array for SingleItem elements
public List<SingleItem>[] ItemList;

void Awake(){

       // Initialize 3 entries in ItemList
       ItemList = new List<SingleItem>[3];

       // Set (int capacity of each entry)
       ItemList[0] = new List<SingleItem>(5);
       ItemList[1] = new List<SingleItem>(10);
       ItemList[2] = new List<SingleItem>(15);

       // add some values
       var Item = new SingleItem();
       Item.ID = 100;
       ItemList[0].Add(Item);

       Item.ID = 200;
       ItemList[0].Add(Item);

       // 2 Elements was added in ItemList[0]
       Debug.Log("ItemList[0].Count " + ItemList[0].Count);
       Debug.Log(ItemList[0][0].ID + " " + ItemList[0][1].ID);

       // Debug result
       // ItemList[0].Count = 2 
       // 200 200 ????????

}

You only ever call new SingleItem() once, which means that there’s only one instance in your whole program. Your list contains two references to the exact same object in memory. When you change its ID to 200, you were just changing the pre-existing instance, rather than creating a new instance. So even before the second call to Add(Item), the first item’s ID would now be 200, not 100.

Note that a large portion of this behavior is because SingleItem is a class, not a struct. Classes are reference types, so when you assign them to things, you’re assigning just a reference to the object, rather than copying all the values stored inside it to a new instance. Structs, on the other handle, are value types and will do a shallow copy of all their values when you do an assignment (which is done internally by the Add() method). So if you had made SingleItem a struct, you’d probably see the behavior you expected. In this case, anyway; you might actually want reference behavior in some cases though, so perhaps making it a class is correct, but you just have to create multiple instances of it when populating the list.

4 Likes

And just for completeness sake this is what you need to do to get the behaviour you want with classes:

  var Item = new SingleItem();
       Item.ID = 100;
       ItemList[0].Add(Item);
       // we need to make a brand new item
       item = new SingleItem();
       Item.ID = 200;
       ItemList[0].Add(Item);
1 Like

Thanks…

Thank you… I understand the reference behavior you have wrote in half.
Does that mean, each ItemList[0][0-5] element is referenced to the same var Item memory by ItemList[0].Add(Item); ?

The benefit of ItemList[0] = new List(5 ← );
The the goal was to add elements in ItemList[0][0-N] until N without reconstruct stuff in memory (prevent GC’s)

If I create a struct if SingleItem by

struct SingleItem {
        public int ID { get; set; }
        public int Damage { get; set; }
    }

Than I got a inconsistent accessibility in public List[ ] ItemList;
Hmm I don’t know to get Item than physically stored into memory.

Thank you too. That works so far. It is a bit irritating that each new item = new SingleItem(); :slight_smile: would create another physical memory reference because of the same var name. Would it be possible to serialize ItemList[ ] without any side effects? For sure I would like to have the ItemList[ ].add() without a GC.

I’m a little confused. You can’t have a list of singleItems that are all the SAME single item. Otherwise whats the point of a list? If you want 100 different single items your going to have to allocate 100 different spots in memory to hold these 100 singleItems. Otherwise they would just be overwriting each other’s data if they were at the same spot in memory. So 100 spots in memory == 100 instances of SIngleItem that will involve some GC to clean them up when your done using them. There is literally no way around this.

Also this quote:

Created a new SingleItem has nothing to do with the fact we are reusing the same variable name. Thats just a place holder for the reference to the memory we allocate with new. You could use a different variable name each time you created a new SingleItem(it would be terrible code because it would be hard to read), but it would effectively be the same thing as using the same variable name. Your having to create a new SingleItem strictly because you want all 100 items to be different and able to maintain different data.

Imagine a teachers’ lounge with a slot on the wall for every teacher. Each one can store messages for that teacher. If you got rid of all 100 boxes and just made 1 box. Everytime you put Alice’s message in the box, it will throw away Bill’s messages. It just wouldn’t work.

1 Like

Well, if SingleItem were a struct, allocating a List with a capacity will allocate a single contiguous chunk of memory that will contain multiple instances of SingleItem without the overhead of multiple allocations. But otherwise I’ll agree that everything you say is spot on.

To Quantum1000, I’d suggest not worrying too much right now about avoiding pressure on the GC, as it is a complex subject and requires a deeper familiarity with the language than you’ve currently acquired. You’ll get there, but for the moment I’d recommend focusing on making it simply work. It’s most likely not an issue anyway, and even highly experienced programmers are often (but not always) better off making it work first, and only worrying about performance specifically in the areas that prove to need the attention.

1 Like

Yes for sure I want to have in my case 5 SingleItems in the memory to spot each by the List[×].

// ListItem[0][0]Count = 5
ListItem[0][0].ID = 100
ListItem[0][0].DAMAGE = 56
ListItem[0][1].ID=NULL
ListItem[0][1].DAMAGE=NULL
ListItem[0][2-4]

//ListItem[1][0].Count = 10
ListItem[1][0].ID = 101
ListItem[1][0].DAMAGE = 12
ListItem[1][1].ID = 103
ListItem[1][1].DAMAGE = 62
ListItem[1][2-9].ID=NULL
ListItem[1][2-9].DAMAGE = NULL

// we need to make a brand new item
item = new SingleItem();

That works fine!

That means ItemList[0] = new List(5); adds 5 NULL references to ItemList[0] ?
ItemList[0].Add(Item) add the reference and memory value from new Item?
That would explain why I got the same value without an additional “item = new SingleItem();” to me.

I’m exactly at this stage Andy. All this stuff have worked well with a simple array. If ItemList[ ] is completely filled, I convert it by ToArray.

Not exactly. If you did this:

ItemList[0] = new List<SingleItem>(5);
if (ItemList[0][0] == null)
     Debug.Log("Its null");

It would return an ArgumentOutOfRange Exception.

When you make a list it allocates a default amount of memory to handle adding elements to you list. That way all its items are contiguous in memory and it can do tricks to iterate through them. If you have enough room for 10 and add and 11th it will probbaly expand its memory out to 20, then add the 11th item. When you give it a specific number you saying allocate some memory for 5 items. But it doesn’t actually create any items yet. So there are no references or even space to hold those references. In general unless you really know whats going on underneath the hood, there isn’t much reason to tell the List how much space to set aside for itself.

1 Like

Thanks a lot.