Is there a better way to add an array element?
Monkey Forums/Monkey Programming/Is there a better way to add an array element?
| ||
I basically always end up doing something like this:Field sprites:TSprite[] Method AddSprite(s:TSprite) sprites = sprites.Resize(sprites.Length() +1) sprites[sprites.Length - 1] = s End Method I know if speed was a factor I could create the array with a certain number of slots and only make new slots all in a batch when I needed the array to grow more. |
| ||
You could use a stack? This handles the array resizing for you and you can still access the data like a standard array. You could even extend your own to hack into the data structure to gain greater control over the data array. |
| ||
Hmm good plan thanks, I'll look into it. I wonder how Stack speed compares to my array resizing code up there? |
| ||
If you look at Stack module, it's pretty clear. The key lines are:If length=data.Length data=data.Resize( length*2+10 ) Endif So you get a default of ten elements first time you put something in a stack, then it doubles every time it fills up. |
| ||
Thanks Gerry. OK so it's probably a bit more efficient than my method and also I don't have to keep rewriting that resize and add code. |
| ||
brl.Pool uses a stack, but also implements Free() and Allocate() methods, which manages used/unused objects rather than allowing them to be GC'ed. |
| ||
The Pool class isn't documented (V70e). How long has it existed? |
| ||
Why not use a list? |
| ||
A list is going to be slower and potentially thrash the GC. |
| ||
But surely removing elements would mean you have to move the elements after it down the array and then shrink the array. |
| ||
I hadn't realised Pool exists. Seems clear enough from the code though. If you make a pool of N objects of type T, you can use pool.Allocate() instead of New T to get back a (possibly previously used) one. Use pool.Free() to return an object you are finished with to the pool for future use. Ignore the following: ****************************************** You seem to be limited to the initial capacity in this version, so why not use an array? I would have thought you would want to grow the stack in case of a pool being completely used up. But looking at the Stack code it's hard to see how to do that easily at present as there is no Stack.Capacity:Int(). So maybe Mark is waiting to add that method to Stack, then modify Pool.Allocate(). Or he might be considering whether to just use a simplified Stack-type code rather than an actual Stack for Pool. It doesn't need Enumerators etc. after all, so it can be short. ****************************************** Edit: Ignore the previous two paragraphs, I just realised Pool will work fine as is. If you allocate more items than the current capacity, the stack only grows when they are freed - but that is fine, it's only purpose is to preserve them from GC anyway, and GC only becomes a possibility when they are no longer in use. |
| ||
@EdzUp[GD] I'm just building an array to access via an index, I'm not removing elements, which is why I chose an array not a list. List does has a ToArray method but I bet that is slow. Wouldn't want to call it every frame for drawing tons of sprites for example. Well Pool sounds interesting. I made a pool of particle objects (myself using two lists) a while ago, so it's cool if Monkey is going to add a decent class instead as I guess it will be faster. Is it iteratable? I don't have V70 yet. |
| ||
It's in some earlier versions, look in brl modules. Pool isn't iterable as stands, though you could easily make one that was. That would probably be a bad idea, though, as not all the objects you have allocated will necessarily be in the pool. They are only forced in the pool when they are freed. Edit: In fact it's worse than that - objects that are in use are *never* in the pool! The point of a pool anyway is just to avoid GC by re-using objects. You would keep them in a different data structure such as a list. You could probably created some kind of PooledList template that combines a Pool and a List! |
| ||
Not sure where the docs went for brl.pool - adding them back now! Pool is not 'iterable', because it doesn't know what sort of container you want the objects to be used in - sometimes you want to use a list, sometimes a stack, sometimes a map etc. It's really just a replacement for 'New' to limit GC. Here's an example... Import brl.pool Class Actor Function Create:Actor( x:Float,y:Float ) Return _pool.Allocate().Init( x,y ) End Method Destroy:Void() _pool.Free( Self ) End Method IsDead:Bool() Return _t=0 'timeout? End Method Update:Void() If _t>0 _t-=1 'update timeout End Private Field _x:Float Field _y:Float Field _t:Int Global _pool:=New Pool<Actor>( 1000 ) Method Init:Actor( x:Float,y:Float ) _x=x _y=y _t=Rnd(20,100) 'random timeout Return Self End End Function UpdateActors:Void( actors:Stack<Actor> ) 'Update all actors For Local i:=0 Until actors.Length actors.Get( i ).Update() Next 'Flush dead actors Local alive:=0 For Local i:=0 Until actors.Length Local actor:=actors.Get( i ) If actor.IsDead() Print "Dead!" actor.Destroy() Continue Endif actors.Set alive,actor alive+=1 End ' actors.Length=alive 'v70 only... While actors.Length>alive actors.Pop() Wend End Function Main() Local actors:=New Stack<Actor> For Local i:=0 Until 100 actors.Push Actor.Create( Rnd(100),Rnd(100) ) End While actors.Length UpdateActors actors Wend Print "Done!" End Note: You could actually combine the update-all/flush-dead steps in UpdateActors into a single pass. However, I've separated them because it's likely that Actor.Update may need to access the (probably global) actors stack and, if it was allowed to access destroyed actors in the process, bad things could happen... |
| ||
Great, thanks for the explanations Mark and Gerry! |
| ||
Just one note in case it's not obvious: If you use Pool.Allocate() for an object instead of New(), don't expect fields to be nicely zeroed out as they would be after New(). They may contain data from a previous object. Forgetting this could easily produce annoying bugs that only appear after your code has run for a while. |
| ||
Yep good point. For my particle pool I have a function that specifically zeroes out the fields before use. |