gSkinner - Home

AS3: Dictionary Object

Posted on July 10, 2006 by Grant Skinner

I think one of the handiest new features in ActionScript 3 is the Dictionary object, which lives in the flash.utils package. It is a new object type that allows you to associate a value with an object key. This is similar to how array associates values with numeric indexes, and how you can use a generic object to associate a value with a string key.

// Arrays use numeric indexes:
var arr:Array = new Array();
arr[0] = "value";
// Generic objects use string indexes:
var obj:Object = new Object();
obj["key"] = "value";
// Dictionary uses object keys:
var dict:Dictionary = new Dictionary();
dict[myObj] = "value";


It’s important to understand that Dictionary uses strict equality to match the object, not the reference, as the key. This means that different references to the same object will act as the same key:

import flash.utils.Dictionary;
var a:Object = new Object();
var b:Object = a; // reference to the same object
var dict:Dictionary = new Dictionary();
dict[a] = "fun!";
trace(dict[b]); // traces 'fun!' because a===b

You can use any type of object as a key, not just generic objects (you could use a Sprite, or an Array, for example). This includes primitives (string, boolean, number, int, uint) which are matched based on value (again, strict equality):

var dict:Dictionary = new Dictionary();
dict["string"] = "joy!";
trace(dict["string"]); // traces "joy" because "string"==="string"

Dictionary objects are enumerable with “for in”, and “for each” loops:

for (var key:Object in dict) {
// iterates through each object key
}
for each (var value:Object in dict) {
// iterates through each value
}

You can also set a Dictionary to use weak references as keys. This is pretty cool, because it means that if you clear all references to an object except those in a weakly referenced dictionary, that object will be available for garbage collection, which in turn will release the reference to its value.

var a:Sprite = new Sprite();
// create a weakly referenced dictionary by passing true as first param:
var dict:Dictionary = new Dictionary(true);
dict[a] = new Object();
a = null; // clear original reference to the Sprite.

In the above example, the Sprite is now available for collection, which in turn will free the Object for collection. This can be a very handy tool for creating memory management tools, and anywhere that you want to keep lists of objects but not interfere with their collection.

Dictionary objects can be really handy for maintaining object lists / queues, such as a doLater queue for functions (though please note the bug mentioned below), a listeners list for a custom event system, a list of Sprites in a game, or a list of row-renderers in a list component. You could cross-reference an array and a dictionary for quick look-ups in ordered lists (ex. a depth manager) – the array would hold references to objects in the appropriate order, and the dictionary would hold array indexes keyed to the objects themselves.

It can also be used for associating meta data with sealed objects. For instance, a layout manager might need to store extra data about the components it is managing, so instead of injecting that data into the component arbitrarily (which won’t work in AS3 anyway), it can maintain a Dictionary that uses weak references to the components as keys, and stores the extra data as the values. This opens the door to a lot of interesting new options from a code architecture perspective.

Note that there is a known bug with Dictionary that prevents it from operating correctly with references to methods. It seems that Dictionary does not resolve the method reference properly, and uses the closure object (ie. the “behind the scenes” object that facilitates method closure by maintaining a reference back to the method and its scope) instead of the function as the key. This causes two problems: the reference is immediately available for collection in a weak Dictionary (because while the method is still referenced, the closure object is not), and it can create duplicate entries if you add the same method twice. This can cause some big problems for things like doLater queues.

I’ll be discussing more about how you can use Dictionary objects with weak references in my next article on resource management.

Follow @gskinner on Twitter for more news and views on interactive media.
34 Comments

this sounds very interesting, but I have 1 question: if I have used an object as the key, then later on the object gets garbage collected, if I "for each" through the dictionary has the weak reference just vanished or will there be some residual trace of it (like a null key or something)?

Posted by: chris on Jul 11, 2006 3:17am

:( very confusing. Could you please explain in layman's terms...like how something was done in AS2 and how Dictionary class can help in replacing that to make life simpler.

Posted by: Supriya on Jul 20, 2006 1:16am

"like how something was done in AS2 and how Dictionary class can help in replacing that to make life simpler."

Say you need to check a couple of objects for something (nodes in a pathfinding routine for intsance), and you want to keep track of which objects have been checked this frame. You could set a checked-parameter to true on all of them, which means you'll have to set it to false for the next time you go through the objects.

Or you could store them in an array - ie checkedObjects.push(someObject) but to see if an object has been checked, you'd need to loop through everything to see if the current one has been checked - unless you give them all a unique index or something.

Enter the dictionary. Just do checkedObjects[someObject]=true and to see if it has been checked you can do if(checkedObjects[someObject]) { etc }. Just delete the reference to the checkedObjects dictionary after you're done.

That's just one example that comes to mind. I'm using it for my tile engine too, and there's tons of possible applications for it.

A few days ago, I did some benchmarking, and came to the conclusion that a dictionary is in fact faster than an array, but it was a quick-and-dirty test so I wouldn't call it conclusive at all. I'd like it if Mr Skinner, or anyone else could verify this.

Posted by: GameSQUID on Jul 20, 2006 8:29am URL: http://www.gamesquid.com

Ow, and another thing that's cool about dictionaries is that you can easily delete an object from it.

If you'd use an array, you would have to loop through it to find the index of the object, and splice the array there. In a dictionary, since the objects are the keys, you can just delete myDictionary[someObject], which is easier, and probably faster (haven't checked, but it should be, especially when there's lots of data in it).

Posted by: GameSQUID on Jul 20, 2006 8:34am URL: http://www.gamesquid.com

This is very cool I use a function like mentioned above that I dance through arrays regurlarly to look up an object base on instance name of the movie clip. This would be great for going directly to the OBject instance based on the object itself. And not have to loop through an array and match it based on name. I do like doing look up's using Named Value pairs. But this seems much more poweful looking for an object match instead. VERY NICE! Thanks Grant!

Posted by: Metallikiller on Jul 31, 2006 10:59pm URL: http://www.everything4me.com/comsvr/

Hi Grant,

I'm enjoying your blog, thanks!

For the "key matching by value" issue, however, I've come to different results. In my experience, the comparison is done how it's always done in AS: primitve data types are compared by value while composite types are compared by reference. If we use, for example, Strings as key (even if we type them as Objects), the comparison is done by value:

var s1:Object = "string";

var s2:Object = "string";

trace("s1==s2:"+(s1==s2));

This is not the case for objects. See this example:

import flash.utils.Dictionary;

var a:Object = { x:3 };

var b:Object = { x:3 };

var c:Object = a;

var s1:Object = "string";

var s2:Object = "string";

trace("a==b:"+(a==b) + " a===b:"+(a===b));

trace("a==c:"+(a==c) + " a===c:"+(a===c));

trace("s1==s2:"+(s1==s2) + " s1===s2:"+(s1===s2));

var dict:Dictionary = new Dictionary();

dict[a] = "aaaa";

dict[b] = "bbbb"; // does not overwrite dict[a] because b's a different object (comparison by reference)

dict[c] = "cccc"; // overwrites dict[a]

dict[s1] = "s1";

dict[s2] = "s2"; // overwrites dict[s1] because of string comparison (by value)

for (var key:Object in dict) {

trace("dict["+key+"] = "+dict[key]);

}

Strict comparison does not influence whether the comparison is done by value or by reference but rather says the operand must be equal in value *and* type.

Posted by: david on Aug 9, 2006 3:24am URL: http://www.illustree.com

i don't believe the weak reference option for the dictionary class works. i used the following test (because i can't your duplicate localconnection to work):

first, in the authoring environment i create a movieclip with a few frames and attach a trace(this) on the last frame and assign it a class, testClip. it's not placed on-stage.

next, attached to the first frame of the main timeline i use:

//******** begin code ************

var mc:MovieClip=new MovieClip();

var dict:Dictionary=new Dictionary(true);

dict[mc]=new testClip();

mc=null;

//delete dict[mc];

// the code below is to force garbage collection

var t:Timer=new Timer(100,0);

t.addEventListener(TimerEvent.TIMER,f,false,0,true);

t.start();

function f(evt:TimerEvent) {

for (var i:int=1; i

Posted by: kglad on Jun 24, 2007 9:34am

that gc forcing code should be:

var t:Timer=new Timer(100,0);

t.addEventListener(TimerEvent.TIMER,f,false,0,true);

t.start();

function f(evt:TimerEvent) {

for (var i:int=1; i

Posted by: kglad on Aug 6, 2007 9:14pm

there must be some formatting issue with this forum that causes that code to be mangled.

Posted by: kglad on Aug 10, 2007 9:04pm URL: http://www.kglad.com

kglad, I have written a Queue class [doLater] utilizing a Dictionary Object for storage of functions and their parameters. The weak referencing does indeed work. You can find the source for my Queue class here -> http://blog.efnx.com/?p=37 . Using that class in another fla it's easy to see the functions instantly being gc'd with this code:

import com.efnx.utils.Queue;

function blah(val1:int, val2:int):void

{

trace(val1+val2);

}

function blah2(val1:int, val2:int):void

{

trace(val1+val2);

}

var queue:Queue = new Queue(true); //

Posted by: Schell on Dec 8, 2007 1:39am URL: http://blog.efnx.com

How to find out the number of elements in Dictionary without iterating through each object/key?

Posted by: Ilya on Feb 7, 2008 12:51am

There is no length property for a Dictionary so I use a separate counter such as numElements:uint

It's a few extra lines but iteration is much more expensive.

Posted by: Anna on May 9, 2008 9:30am

not loving the fact that it provides no access methods.

I do appreciate that it is enumerable, but the lack of other functionality common to arrays, collections and lists seems to make it only useful as a basic lookup...

Posted by: Nicholas on Jul 9, 2009 10:38am URL: http://selectedWork.ghostmonk.com

Excellent, thanks!!!

Im building a dynamic web form(like web browser) for a flash site im working on using values from the existing xml file, which works off references all over the shop.

Ive been trying to solve a bug for ages using object references on the standard Object().

ie. _objects[some_sprite_object] = "string value"

Then i came across this. Saved hours of debugging and pulling hair out.

Posted by: Scott Smith on Jul 9, 2009 4:17pm URL: http://www.c2centertainment.com

"This causes two problems: the reference is immediately available for collection in a weak Dictionary (because while the method is still referenced, the closure object is not), and it can create duplicate entries if you add the same method twice."

I can demonstrate the first problem. I have seen method (actually closure) keys get garbage collected while the corresponding class instance is still referenced.

But I cannot demonstrate the second problem. I add the same method twice as a key, and I always only get one entry in the Dictionary. Can you provide some code that demonstrates the second problem?

If the second problem is true, then I should never pass a Dictionary instance methods for keys, because I could create duplicate entries (although I've never seen that actually happen, at this point.) If only the first problem is true, I can still pass a Dictionary instance methods for keys, as long as I don't use weak keys. I'd like to know if I really need to stop using Dictionaries with methods (closures) as strongly-referenced keys.

Posted by: Dave on Sep 21, 2009 1:53pm

Dave - there's a very good chance that Adobe has fixed the issue with closures in the past 3 years. I'll try to test it again soon, and update this page with my results. Thanks for letting me know!

Posted by: Grant Skinner on Sep 21, 2009 2:16pm URL: http://gskinner.com/blog/

Thanks a lot. While I'm waiting to see if the second problem is true, I thought of a significant consequence of the first problem.

It is impossible to create a passive event-managing utility with a "removeAllEventListeners" function. By "passive", I mean event listeners are garbage collected in exactly the same circumstances as if the utility did not exist. My reasoning is as follows: In order to implement "removeAllEventListeners", I need to keep references to all event listener functions, say as keys in a Dictionary. Those references must be weak or strong.

If the keys in the Dictionary are weak, then method closures will be removed from the Dictionary as soon as the garbage collector runs, even if the corresponding event listeners are not garbage collected and can still be triggered. In this case, "removeAllEventListeners" will not work, because it cannot keep references to the event listener functions.

If the keys in the Dictionary are strong, then the corresponding event listeners can always be triggered. The keys in the Dictionary will prevent the event listeners from being garbage collected, even if there are no other references to them and they would have otherwise been garbage collected. In this case, the utility is not passive - it effectively makes all event listeners strongly referenced for as long as there's a reference to the utility itself.

I've tested this idea, and it appears to be correct. Does this sound right to you?

Posted by: Dave on Sep 30, 2009 4:50pm

I was wrong. It IS possible to create a passive event-managing utility with a "removeAllEventListeners" function. My incorrect assumption was "I need to keep references to all event listener functions." Instead, I can keep references to class instances and the String names of their respective methods that are being used as event listener functions. For example, the class instances are keys in a Dictionary, and the values contain method-name Strings. This way, I don't run afoul of method closures - I use the class instances and method-name Strings to look up a method closure only when I need to remove the event listener.

If the keys in the Dictionary are weak, the class instances will be removed from the Dictionary whenever there are no other references to them and they are garbage collected, that is, in exactly the same circumstances the corresponding event listeners would no longer be triggered if the utility did not exist. So the utility is passive.

Posted by: Dave on Oct 1, 2009 6:31pm

hi and thanks for your community work.

I would like sindyc your fell , but button syndication link on your page don't work on me .

thank

Posted by: bruno on Oct 23, 2009 2:12am

"Dave - there's a very good chance that Adobe has fixed the issue with closures in the past 3 years."

I can confirm that this bug still exists in Flex 3.5 sdk.

Many of us make a living because Actionscript requires so many workarounds that the average coder is not aware of. However, I draw the line at having to define all my event handlers as public. Dave, you are a stronger man than I!

Posted by: Ben H on Feb 21, 2010 4:29pm

Nice blog post. Dictionaries are sweet entities, aren't they?

Posted by: Praveen Ghatta on Apr 1, 2010 3:25am

Can a Dictionary be bound as a datasource for itemRenderer or repeater? I currently use ArrayCollections to fuel the itemRenderer and it works quite well but it sounds like a Dictionary is a more efficient way to handle the data.

This is a helpful post with useful comments.

Thanks,

Dylan

Posted by: Dylan on Apr 23, 2010 9:46am URL: http://www.pinelakedesign.com

Great article! May I just add that AS3 arrays can be associative, numeric or a hybrid of both, meaning array elements can have string or numeric keys.

I have almost completely switched from arrays to vectors though since the latter are a bit faster to iterate through. But yeah, Dictionary does look very promising.

Posted by: Omer Hassan on Jul 16, 2010 8:41am

"You could cross-reference an array and a dictionary for quick look-ups in ordered lists (ex. a depth manager) - the array would hold references to objects in the appropriate order, and the dictionary would hold array indexes keyed to the objects themselves."

That's interesting! Can you give an implementation example? I'm trying to write a class extending the Array, that also contains an Object for quick access to elements by name (no need for a Dictionary, since string are used for keys)... Rewriting the push() and all other methods, so on surface it will look like an array... But got a bit stuck...

Basically want a normal array with length and for loops, but extended with a function getItemByName()

Posted by: Dis on Sep 7, 2010 7:44am

I must be particularly thick, but I'm having trouble wrapping my head around the advantages of using a Dictionary, other than the (debatable) performance buffs.

In an earlier example, you mention using a Dictionary to check objects for something. To me, this has always been possible (with objects built on dynamic classes - which most native, non-primitive types are) by adding a property to the object itself such as "hasBeenChecked"; and if you are the creator of the class you could build that property in as a public property, or probably better, use a private property along with getters (and setters if necessary), and possibly even integrating the "checkObject()" method within the class.

I mention all this simply because I'm trying to find an example of when you would need to iterate over a collection of different types of objects, some of which would not be dynamic classes, whose class source codes you have no control over and which cannot be wrapped into some kind of superclass that could handle the kind of checking you describe.

This is probably all very narrow-minded and I'm sure some really convincing examples of Dictionary usage exist. I was hoping you or one of your readers might point some examples out.

Fantastic posts, as always!

Tom

Posted by: Tom Auger on Sep 24, 2010 4:06pm URL: http://www.tomauger.com

Heya,

Just been trying to put this to use in Flash CS5, but when looping over using, for in, compiler is complaining like a whore about my key being a string, not an object. I've just landed on this prob and either gonna look else where, or make my own type, just wondering if anyone else has this prob and if its just me screwing up my type casting some how!?

Code is simple, init dic add Sprites values using Point types as keys, loop over dic with tmpPoint:Point as key... ect simple stuff...

Posted by: chris on Mar 21, 2011 12:56am

Hi Chris,

The key needs to be typed as Object. You can then cast it within the for...in block:

for (var key:Object in dict) {
var point:Point = Point(key);
...
}

Posted by: Grant Skinner on Mar 21, 2011 9:15am

@Dis I wrote something similar. Check it out: http://github.com/rafaelrinaldi/list

Posted by: Rafael Rinaldi on Jul 22, 2011 4:07pm URL: http://www.rafaelrinaldi.com

"Or you could store them in an array - ie checkedObjects.push(someObject) but to see if an object has been checked, you'd need to loop through everything to see if the current one has been checked - unless you give them all a unique index or something."

I do a check with indexOf(object) before I add complex types to an array. indexOf is the fastest way to access an array in an array if you know what it is you are looking for. It uses strict equality to ensure values are unique, and will return -1 if a value has no index.

Using that I would try to resolve the issue of event listeners and dictionarys by using an array or vector instead. Any time I would need to add a listener I would later want to remove, I can use indexOf to check the vector or array then run whatever code to remove the listener and pop then nullify that reference. Just wonder if that would still lead to a bug with dynamically adding and removing event listeners

Posted by: frozenbinary on Dec 9, 2011 5:07pm

RE: "Note that there is a known bug with Dictionary that prevents it from operating correctly with references to methods. It seems that Dictionary does not resolve the method reference properly."

I have a good news, just made a test and there is no bug in FP 11.1.102

here is a test code:

MemTest.as

package  {
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.Dictionary;

public class MemTest extends Sprite {
private var d:Dictionary;
public function MemTest() {
d = new Dictionary(true);
addEventListener(Event.ENTER_FRAME, onEf);
}
private function onEf(e:Event):void {
for (var n:uint = 0; n < 20000; n++) {
new MemTestA(d);
}
}
}
}


MemTestA.as
package  {
import core.Messenger;
import flash.utils.Dictionary;
public class MemTestA {
public function MemTestA(d:Dictionary) {
d[mm] = 1;
}
private function mm():void {}
}

}



P.S.
here is an interesting link about dictionary documentation understanding http://jacksondunstan.com/articles/1380

Posted by: Good on Sep 3, 2012 6:15pm URL: http://www.artcraft.cz

my mistake, bug is there,
here is article about it: http://mikecann.co.uk/personal-project/as3-dictionary-weak-method-closures/

Posted by: Good on Sep 4, 2012 9:58am URL: http://www.artcraft.cz

Too bad as3 can't parse a dictionary to json like python. :/

Posted by: eddie on Jul 26, 2013 1:27pm

Funny, after so many years this is still the #1 search result for as3 dictionary :)

Posted by: B.K. on Jan 27, 2014 9:05am URL: http://www.xida.de

Great post! What's the best way filesize-wise too make an object have many keys with equal values? Thanks!

Posted by: sam on Apr 27, 2014 12:38am