This post is the continuation of the basic knowledge article Why we need a collection framework in ActionScript and introduces you to the benefits of collections more from a technical perspective.
Note to the performance tests in this article
This article contains a number of performance test results. The tests have been executed using a small testing harness which can be found at the sibirjak google code page. The column header of the tables showing those results use the following abbreviations. The measured time is displayed in milliseconds.
| + | add |
| +A | addFirst |
| Z+ | addLast |
| - | remove |
| -A | removeFirst |
| Z- | removeLast |
| ? | has |
| ?k | hasKey |
| -k | removeKey |
Definitions and overview
Definitions
First of all a small definition of the term collection used in this article. A data container is meant any data structure that holds a number of distinctive objects in order to return at a later time. The built-in data containers in Flash are Array, Vector, Object and Dictionary. A collection is then furthermore a data container …
- with a generalised API that fully abstracts from the actual implementation
- and a high degree of reusablility (not narrowed to a special operation purpose).
Collections do not try to replace the built-in containers but aim to extend those with functionality and common algorithms. Many programming languages carry a collections framework in its core libraries. In ActionScript we unfortunately need to implement collections individually. This situation has brought us already a lot of approaches to establish an ActionScript collections framework.
Common reasons to use collections
Besides the availability of a convenient API, collections are preferred to add specific functionality, to gain reliability or to increase the performance of certain operations. Especially the latter might be confusing at the first glance. Indeed, all built-in containers have their specific implementation and therefore distinctive benefits and disadvantages.
This article shows how the choice of an appropiate collection may improve the runtime performance of ActionScript applications by providing interesting examples and performance test results. The article starts with a description of situations where we typically use containers to collect data and what the different requirements to such containers are. The second part examines the built-in Array and Dictionary and how they can fit the indentified requirements. The third (and last) part discusses several collections and common approaches to implement them.
Aspects of using data containers
Software applications may need to collect data for various reasons. Each of those reasons implies a preferred way how elements are stored, retrieved or removed from the data container. Different problem situations may be solved best using an adequate container or vice versa, there is no multipurpose container that fits all kinds of requirements. This chapter shows a random list of situations where we typically use data containers.
Embedded inventory
The most obvious use case of collecting data is to make up a (hard coded) inventory which serves as a data base in the class or even in the entire application. The data in the inventory should have a stable order and provide at least fast sequential access (from the first to the last item).
1 2 3 4 5 6 7 8 | var users : Array = [ new User("Mike", "Donner"), new User("Susi", "Sonnenschein") ]; for each (var user : User in users) { // do something } |
Proxy
A proxy is an internal representation (or a cache) of data stored elsewhere, e.g. on a remote server. The proxy should use the same ordering as the orginal repository. Since data between client and server is exchanged and identified using an object ID, we like to address (find, add, remove) items in the proxy by using IDs.
1 2 3 4 5 6 7 | var users : Array = UserService.getUsers(); var user : User; for each (var id : String in users) { user = UserService.getUser(id); // do something } |
Relations between objects
One-to-many relations can be expressed using a link table. The table do not need to have an order but should allow fast look up a relation by a given object.
1 2 3 4 5 6 | var friends : Dictionary = new Dictionary(); ... friends[user1] = [user2, user4]; friends[user2] = [user1, user3]; ... var friends : Array = friends[user3]; |
Wrapper
Encapsulated components often store data in its own fashion by adding specific information to the given data objects using composition. To enable the user to operate the component with the orginal data, the component hosts an internal look-up table which maps objects to its wrappers.
1 2 3 4 | loader.add("data.xml"); ... // decide not to load the data loader.remove("data.xml"); |
Grouping objects
To return a group of objects from a method (or to pass to) we use a container. In the most of the cases the order of items returned is important.
1 2 3 4 5 6 | function getVisits(session : String) : uint { var session : Session = AuthService.getSession(session); var user : User = UserService.getUser(session.userId); ... return [user.name, session.visitCount]; } |
Temporary containers
Often we set up local containers temporarily to find duplicates, to count instances, to store items matching a certain criterion or to prepare a data structure before further processing it. Sometimes a plain array does the work, sometimes a look-up container such as Object or Dictionary. In a number of cases we need a combination of both to preserve the original order while achieving fast look-up.
1 2 3 4 5 6 7 8 9 10 11 12 | var array : Array = [1, 3, 2, 1, 3, 1, 1, 4]; var map : Object = new Object(); var unique : Array = new Array(); for (var i : uint = 0; i < array.length; i++) { if (map[array[i]] === undefined) { unique.push(array[i]); map[array[i]] = true; } } trace (unique); // 1,3,2,4 |
More specific software problems
A queue is an ordered container where we want to add at end and to remove at start. In a stack we add and remove always “at end”. A searchable index maintains a sort order, and items shall be addressed instantly by instance. And so on.
1 2 3 4 5 6 7 8 9 | queue.add("users.xml"); queue.add("products.xml"); queue.add("prices.xml"); ... var url : String = queue.poll(); // users.xml // load ... url = queue.poll(); // products.xml // load |
Binding data to user interfaces
Data driven user interface controls (List, SelectBox, DataGrid) often shall be updated automatically if the assigned data source changed. The data container then needs to dispatch change notifications.
Summary
The illustrated situations above show different and often mutually exclusive requirements to the desired data structure solution:
- The kind of order of the items (no order necessary, order by insertion, sort order).
- Sequential (for-loop, iterator) or random access (index, key, item by instance) to the items.
- High performance of adding and removing in front (or in between or at end) of a sequence of items.
- High performance of finding an element.
- Fast access to the lowest or highest element in a collection.
- Permission of multiple instances of an element (or not).
- Mapping of items by preserving a particular order of the entries.
- Enabling data binding by dispatching change notifications.
In the most of the situations we especially count only on a specific feature of a data container. This leads to the question if there are containers that fit the particular requirements better than others. The next chapter discusses how the Flash built-in data containers Array, Object and Dictionary might be used (or not) to solve common data handling problems.
Array and Dictionary – Features and Weaknesses
The Array
The Array is apparently the fastest possibility of storing elements sequentially. However, adding to (or removing from) an array requires the array to internally shift the position of all succeeding items by the number of elements added. This shifting is the more expensive the more the insertion occurred in front of the array and the longer the array is. Michael Baczynski (polygonal.de) has visulalised this behaviour in its linked list example.
Another consideration (and common trap) is the time required to find (or remove) an element within an array. Since the array is not a look-up container, the array needs to test each item for equality which is the more expensive the more longer the array is.
The table below shows the runtime of adding or removing at start or at end as well as the performance of the look-up operations has and remove.
| TestCase | Z+ | Z- | +A | -A | ? | - |
|---|---|---|---|---|---|---|
| 5000 Items | ||||||
| Array | 0 | 0 | 5 | 6 | 157 | 64 |
| 10000 Items | ||||||
| Array | 0 | 0 | 23 | 25 | 441 | 252 |
| 50000 Items | ||||||
| Array | 2 | 1 | 738 | 770 | 11238 | 6070 |
Keeping an array sorted
In many cases a container that maintains a sort order is desired. Such a container stores and returns its items always based on a sort critlerion. Applications could be a phone book, a high score list, a performant searchable index or a priority queue. A simple implementation would be to sort an array after each insertion operation. However, this approach does not scale as the following table shows.
| TestCase | + |
|---|---|
| 100 Items | |
| Array | 17 |
| 500 Items | |
| Array | 630 |
| 1000 Items | |
| Array | 2746 |
| 2000 Items | |
| Array | 12459 |
Another consideration regarding the sorted array. The built-in sort method does not preserve the formerly order of equal items. If it is important to sort equal items in the order they were inserted, the Array.sort() method cannot be used.
1 2 3 4 5 | var array : Array = [1, 2, 3, 4, 5, 6, 7, 8, 9]; array.sort(function(item1 : *, item2 : *) : int { return 0; // all items are equal }); trace (array); // 5,2,3,4,1,6,7,8,9 |
The Dictionary
The Dictionary is a look-up container where elements can be accessed instantly using a unique key. The Dictionary maintains no order of items and does not provide information about the number of elements contained. Operations based on keys perform in nearly constant time. Finding (or removing) of items is significantly slower than done with an array.
| TestCase | + | ?k | -k | ? | - |
|---|---|---|---|---|---|
| 5000 Items | |||||
| Dictionary | 1 | 1 | 4 | 481 | 437 |
| 10000 Items | |||||
| Dictionary | 1 | 1 | 3 | 1900 | 1732 |
| 50000 Items | |||||
| Dictionary | 10 | 4 | 72 | 49073 | 42916 |
Another consideration of the Dictionary is its missing type safety. While everything is going well with complex keys, a primitive key is stored by its string representation rather than its original value as the following code example shows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var dict : Dictionary = new Dictionary(); dict[1] = "one_int"; dict["1"] = "one_string"; dict[true] = "true_boolean"; trace (dict[1] !== undefined); // true trace (dict["1"] !== undefined); // true trace (dict[true] !== undefined); // true trace (dict["true"] !== undefined); // true (!) trace (getSize()); // 2 (!) delete dict[1]; trace (dict[1] !== undefined); // false trace (dict["1"] !== undefined); // false (!) trace (dict[true] !== undefined); // true trace (dict["true"] !== undefined); // true (!) trace (getSize()); // 1 (!) function getSize() : uint { var size : uint; for (var key : * in dict) { size++; } return size; } |
The missing type safety may cause serious errors or at least confusion. For example the algorithm to test if two arrays contain the same elements would not work reliably with an unmodified Dictionary. This algorithm populates a map with the number of occurrences of each element contained by both given arrays.
Dictionary used as a set
As shown, Array and Dictionary are basically slow in finding or removing items. Nevertheless, it is possible to assign the actual item (which we want to access instantly) in the key property of a Dictionary entry. All items then have to be unique.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var dict : Dictionary = new Dictionary(); dict[1] = true; dict["1"] = true; dict[2] = true; trace (dict[1] !== undefined); // true trace (dict["1"] !== undefined); // true trace (dict[2] !== undefined); // true trace (dict["2"] !== undefined); // true (!) delete dict[1]; trace (dict[1] !== undefined); // false trace (dict["1"] !== undefined); // false (!) |
The features of such a set are the same as of the Dictionary: No order, no size information, no type safety … but a striking performance in locating objects
.
Summary
The Flash containers are sufficiently for the most operations. Nevertheless, there are certain situations where performance, functionality and reliability leave something to be desired.
Performance
Adding or removing items in front of an array is a comparatively slow process. Thus, using an array as a queue is not the optimal solution, and a linked list performs much better (as we will see later in this article).
Finding items in an array or in a Dictionary instance requires linear time (the more elements the more time). As shown in the snippets above, we often want to find an item instantly. Fast item look-up can be achieved by using a Dictionary as a set.
Keeping an Array instance sorted calling its built-in sort after each insertion operation is a serious performance leak.
Functionality
The Dictionary does not reveal how many items it contains. Calculating such a size value can be done only with a loop over the entire contents which requires linear time.
The Dictionary does not provide any order of items. Fast key or item look-up (when used as a set) while maintaining a particular order is natively not possible. To anyhow obtain an ordered or sorted map (or set) we need to combine the Dictionary with an additional container that stores the item order.
Neither Array nor Dictionary support data binding. It is possible to wrap both containers to control all insertion or removal operations and dispatch reliable change notifications.
Reliability
The Array sort method is not stable which may cause confusion or even problems.
The Dictionary stores a primitive key always in its string representation. Different keys that have the same string representation are considered as identical (1 = “1″, true = “true”). Algorithms that rely on the Dictionary may fail if not considered.
Common collections and their implementation
Array wrapper (ArrayList)
An array wrapper is the most often encountered collection implementation. The most famous array wrapper is the Flex ArrayCollection. There are many reasons to create an ArrayList.
One goal is to control the way how items are added or removed. For example, it is possible to only permit inseration and removal at the end of an array which results then in a stack realisation.
Another goal is the simplification of the Array interface. For example arrayList.addAt(10, item) is fare more intuitive than array.splice(10, 0, item). By the way, who knows the difference between slice and splice without have an eye at the docs?
Often the array wrapper offers additional methods that easify the array handling (count, removeAll, lastIndexOf). The ArrayList might implement a stable sort that is used instead of the not stable built-in array sort.
1 2 3 4 5 6 | var arrayList : ArrayListRaw = new ArrayListRaw(); arrayList.array = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arrayList.sort(function(item1 : *, item2 : *) : int { return 0; // all items are equal }); trace (arrayList.array); // 1,2,3,4,5,6,7,8,9 |
Basic array wrapper implementation in ActionScript
AS3Commons ArrayList usage example
Performance
The basic array wrapper has nearly the same runtime as an unwrapped array.
| TestCase | Z+ | Z- | +A | -A |
|---|---|---|---|---|
| 5000 Items | ||||
| ArrayList | 0 | 0 | 6 | 6 |
| Array | 1 | 0 | 5 | 5 |
| 10000 Items | ||||
| ArrayList | 1 | 0 | 23 | 25 |
| Array | 0 | 0 | 22 | 25 |
| 50000 Items | ||||
| ArrayList | 2 | 1 | 746 | 773 |
| Array | 2 | 0 | 734 | 777 |
Basic array wrapper tests source code
Array tests source code
Linked list
A linked list is the most important alternative to an array to achieve a succession of elements. The list is made up of mutually linking nodes where a single node is a plain data object holding a reference to its successor, predecessor and the actual data added.
![]()
Adding or removing of items costs always the same (constant) time because only the links between two or three nodes have to be updated. However, the linked list does not support index access (addAt, getAt, removeAt). Finding elements in a linked list is as expensive as in an array (has, remove).
A linked list is the preferred solution to realise a queue interface. Several data structures utilise a linked list (rather than an array) to maintain element succession.
Basic linked list implementation in ActionScript
AS3Commons LinkedList usage example
Linked list at Wikipedia
Performance
As the table shows, the average performance of insertion or removal operations of a linked list is significantly better than of an array.
| TestCase | Z+ | Z- | +A | -A |
|---|---|---|---|---|
| 5000 Items | ||||
| LinkedList | 1 | 0 | 1 | 0 |
| Array | 0 | 0 | 5 | 5 |
| 10000 Items | ||||
| LinkedList | 2 | 0 | 2 | 0 |
| Array | 0 | 0 | 22 | 25 |
| 50000 Items | ||||
| LinkedList | 15 | 1 | 13 | 2 |
| Array | 6 | 0 | 736 | 780 |
Basic linked list tests source code
Array tests source code
Map
The (basic) map is an unordered container enabling fast key look-ups. A typical map implementation uses an array to host the values and a hashing function to calculate from a given key the index position where the value should be added (or can be found). This is actually not trivial, and fortunately we have in ActionScript the Dictionary that hides all the implementation details and can be used to create a map collection.
The map extends the Dictionary to add an instant size information and to fix the problem of type unsafety (shown in the Dictionary example above). String typed keys are separately stored in an Object instance which lets decide if a given primitive key really exists.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var map : MapRaw = new MapRaw(); map.add(1, "one_int"); map.add("1", "one_string"); map.add(true, "true_boolean"); trace (map.hasKey(1)); // true trace (map.hasKey("1")); // true trace (map.hasKey(true)); // true trace (map.hasKey("true")); // false (!) trace (map.size); // 3 (!) map.removeKey(1); trace (map.hasKey(1)); // false trace (map.hasKey("1")); // true (!) trace (map.hasKey(true)); // true trace (map.hasKey("true")); // false (!) trace (map.size); // 2 (!) |
Basic map implementation in ActionScript
AS3Commons Map usage example
Performance
The performance of the basic map is comparable to that of the Dictionary.
| TestCase | + | ?k | -k |
|---|---|---|---|
| 5000 Items | |||
| Map | 0 | 1 | 2 |
| Dictionary | 0 | 1 | 2 |
| 10000 Items | |||
| Map | 1 | 2 | 4 |
| Dictionary | 1 | 1 | 2 |
| 50000 Items | |||
| Map | 9 | 7 | 27 |
| Dictionary | 4 | 3 | 27 |
Basic map tests source code
Dictionary tests source code
Set
The set is in fact a map where only the keys are from interest. The set implementatin follows the same principles as the of the map: An Object is used to store the string typed keys, an instant size informatin is available.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var theSet : SetRaw = new SetRaw(); theSet.add(1); theSet.add("1"); theSet.add(true); trace (theSet.has(1)); // true trace (theSet.has("1")); // true trace (theSet.has(true)); // true trace (theSet.has("true")); // false trace (theSet.size); // 3 theSet.remove(1); trace (theSet.has(1)); // false trace (theSet.has("1")); // true trace (theSet.has(true)); // true trace (theSet.has("true")); // false trace (theSet.size); // 2 |
Basic set implementation in ActionScript
AS3Commons Set usage example
Performance
The look-up operations has and remove operate in nearly constant time.
| TestCase | + | ? | - |
|---|---|---|---|
| 5000 Items | |||
| Set | 0 | 1 | 2 |
| Array | 0 | 109 | 61 |
| 10000 Items | |||
| Set | 4 | 2 | 4 |
| Array | 0 | 440 | 237 |
| 50000 Items | |||
| Set | 10 | 7 | 36 |
| Array | 4 | 11068 | 5861 |
Basic set tests source code
Array tests source code
Sorted array
To get an array to keep a certain sort order it is the most performant solution to add items using the binary search algorithm. The search returns the index position where a given item should be added. Finding an element within the sorted array also uses a binary search.
AS3Commons SortedList implementation
AS3Commons SortedList usage example
Binary search algorithm at Wikipedia
Performance
The AS3Commons SortedList is an application of a sorted array and used here in the performance tests. Adding items to an array using the binary search algorithm (as the SortedList does) is significantly faster than adding items combined with a subsequent sort.
| TestCase | + |
|---|---|
| 100 Items | |
| SortedList | 1 |
| Array (add+sort) | 18 |
| 500 Items | |
| SortedList | 9 |
| Array (add+sort) | 609 |
| 1000 Items | |
| SortedList | 8 |
| Array (add+sort) | 2770 |
| 2000 Items | |
| SortedList | 21 |
| Array (add+sort) | 12529 |
Adding items to a sorted array is slower than appending items to an unsorted array. Finding or removing elements is faster done with a sorted array.
| TestCase | + | ? | - |
|---|---|---|---|
| 5000 Items | |||
| SortedList | 54 | 50 | 52 |
| Array | 0 | 109 | 62 |
| 10000 Items | |||
| SortedList | 139 | 106 | 120 |
| Array | 0 | 439 | 239 |
| 50000 Items | |||
| SortedList | 1023 | 646 | 1027 |
| Array | 2 | 11056 | 6065 |
AS3Commons SortedList tests source code
Array tests source code
Binary search tree
A binary search tree is the composite counterpart to a sorted array. The tree is made up of nodes where a single node is a plain data object holding a reference to its parent node, its left and right child and the actual data added. The value of the left child is always lesser, the value of the right child always greater than the value of a parent node.
![]()
Finding an element in a tree starts with the root node and continues with the left child node if the given value is lesser and with the right node if the given value is greater than the value of the parent node.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function findNode(item : *) : Node { var node : Node = _root; while (node) { if (item < node.item) { node = node.left; } else if (item > node.item) { node = node.right; } else { return node; } } return null; } |
A binary search tree is used (like a sorted array) to achieve a persistant sort order in that finding of elements is comparable fast. The tree might be used as a priority queue where items are stored and returned depending on a given priority. The tree also may be used in combination with other structures to realise a sorted collection such as a sorted map or a sorted set.
The weakness of a binary search tree is its addiction to degenerate. In the worst case the tree consists of only left nodes or only right nodes. Finding elements in a degenerated tree may cost linear time since each node has to be compared. There are several tree implementations that deal with the problem. A treap is one of them.
AS3Commons Treap implementation
AS3Commons Treap usage example
Binary search tree at Wikipedia
Performance
The performance test uses the AS3Commons Treap and SortedList collections. It reveals, the binary search tree is not actually faster than a sorted array. However the sorted array suffers from the bad performance of (adding and) removing items in front of an array in general where the tree operations take the same time at all positions.
| TestCase | + | ? | - | -A | Z- |
|---|---|---|---|---|---|
| 5000 Items | |||||
| Treap | 67 | 57 | 109 | 4 | 4 |
| SortedList | 53 | 49 | 50 | 6 | 1 |
| 10000 Items | |||||
| Treap | 146 | 125 | 236 | 7 | 7 |
| SortedList | 122 | 106 | 118 | 25 | 0 |
| 50000 Items | |||||
| Treap | 894 | 809 | 1613 | 37 | 38 |
| SortedList | 1038 | 630 | 1022 | 773 | 1 |
AS3Commons Treap tests source code
AS3Commons SortedList tests source code
Ordered map
A basic map does not have any succession order but this order may be realised by combining a map with a linked list where the map internally links a given key with a linked list node.
Basic ordered map implementation in ActionScript
AS3Commons LinkedMap usage example
Performance
For order related operations, the ordered map requires about the addition of time of a basic map and a linked list. For example removing at start takes 39ms which is the time needed to remove at start from a linked list (2ms) plus the time needed to remove a key from the map (37ms).
| TestCase | +A | Z+ | -A | Z- | + | ?k | -k |
|---|---|---|---|---|---|---|---|
| 5000 Items | |||||||
| OrderedMap | 2 | 2 | 3 | 2 | - | 1 | 3 |
| Map | - | - | - | - | 0 | 1 | 2 |
| LinkedList | 1 | 2 | 0 | 0 | - | - | - |
| 10000 Items | |||||||
| OrderedMap | 8 | 5 | 5 | 5 | - | 1 | 5 |
| Map | - | - | - | - | 1 | 2 | 5 |
| LinkedList | 4 | 2 | 1 | 0 | - | - | - |
| 50000 Items | |||||||
| OrderedMap | 29 | 27 | 39 | 34 | - | 8 | 41 |
| Map | - | - | - | - | 8 | 8 | 37 |
| LinkedList | 16 | 12 | 2 | 1 | - | - | - |
Basic ordered map tests source code
Basic map tests source code
Basic linked list tests source code
Ordered set
A basic set does not have any succession order but this order may be realised by combining a set with a linked list where the set internally links a given item with a linked list node.
Basic ordered set implementation in ActionScript
AS3Commons LinkedSet usage example
Performance
For order related operations, the ordered set requires about the addition of time of a basic set and a linked list. For example removing at start takes 39ms which is the time needed to remove at start from a linked list (1ms) plus the time needed to remove an item from the set (37ms).
| TestCase | +A | Z+ | -A | Z- | + | ? | - |
|---|---|---|---|---|---|---|---|
| 5000 Items | |||||||
| OrderedSet | 2 | 2 | 3 | 3 | - | 1 | 3 |
| Set | - | - | - | - | 0 | 1 | 2 |
| LinkedList | 2 | 1 | 0 | 0 | - | - | - |
| 10000 Items | |||||||
| OrderedSet | 9 | 8 | 5 | 4 | - | 1 | 4 |
| Set | - | - | - | - | 1 | 2 | 5 |
| LinkedList | 2 | 2 | 0 | 0 | - | - | - |
| 50000 Items | |||||||
| OrderedSet | 28 | 28 | 39 | 31 | - | 8 | 42 |
| Set | - | - | - | - | 7 | 8 | 37 |
| LinkedList | 16 | 14 | 1 | 1 | - | - | - |
While the Array slows down, finding or removing of items is done as fast it can be with an ordered set.
| TestCase | Z+ | ? | - |
|---|---|---|---|
| 5000 Items | |||
| OrderedSet | 3 | 0 | 2 |
| Array | 0 | 110 | 62 |
| 10000 Items | |||
| OrderedSet | 7 | 2 | 5 |
| Array | 2 | 440 | 237 |
| 50000 Items | |||
| OrderedSet | 34 | 8 | 47 |
| Array | 2 | 10999 | 5851 |
Basic ordered set tests source code
Basic set tests source code
Basic linked list tests source code
Array tests source code
Sorted map
A basic map does not have any succession order. A sort order may be realised by combining a map with a binary search tree where the map internally links a given key with a tree node.
AS3Commons SortedMap implementation
AS3Commons SortedMap usage example
Performance
The performance test uses the AS3Commons SortedMap, Map and Treap implementations. For order related operations, the sorted map requires about the addition of time of a basic map and a treap.
| TestCase | + | ? | - | -A | Z- | ?k | -k |
|---|---|---|---|---|---|---|---|
| 5000 Items | |||||||
| SortedMap | 71 | 65 | 98 | 6 | 6 | 1 | 10 |
| Map | 3 | 409 | 410 | - | - | 0 | 2 |
| Treap | 66 | 58 | 108 | 3 | 3 | - | - |
| 10000 Items | |||||||
| SortedMap | 163 | 141 | 232 | 13 | 12 | 1 | 23 |
| Map | 3 | - | - | - | - | 1 | 7 |
| Treap | 144 | 130 | 249 | 7 | 8 | - | - |
| 50000 Items | |||||||
| SortedMap | 947 | 817 | 2135 | 101 | 88 | 7 | 181 |
| Map | 22 | - | - | - | - | 8 | 55 |
| Treap | 890 | 786 | 1855 | 38 | 39 | - | - |
AS3Commons SortedMap tests source code
AS3Commons Map tests source code
AS3Commons Treap tests source code
Sorted set
A basic set does not have any succession order. A sort order may be realised by combining a set with a binary search tree where the set internally links a given item with a tree node.
The difference between a sorted set and a binary search tree is the performance of item based operations. While the tree needs to find a related node within its structure, the sorted set uses an internal Dictionary to instantly access this node.
AS3Commons SortedSet implementation
AS3Commons SortedSet usage example
Performance
The performance test uses the AS3Commons SortedSet, Set, Treap and SortedList implementations. For item based operations the set is significantly faster than the treap. For all other operations, the sorted set requires about the addition of time of a basic set and a treap.
| TestCase | + | ? | - | -A | Z- |
|---|---|---|---|---|---|
| 5000 Items | |||||
| SortedSet | 75 | 0 | 7 | 4 | 4 |
| Set | 1 | 1 | 2 | - | - |
| Treap | 70 | 58 | 110 | 3 | 4 |
| 10000 Items | |||||
| SortedSet | 153 | 1 | 15 | 10 | 9 |
| Set | 3 | 1 | 4 | - | - |
| Treap | 149 | 132 | 246 | 7 | 8 |
| 50000 Items | |||||
| SortedSet | 916 | 7 | 133 | 68 | 71 |
| Set | 15 | 7 | 33 | - | - |
| Treap | 892 | 813 | 1635 | 38 | 38 |
Item based look-up operations (has, remove) are faster in a sorted set than in a sorted array. Removing of items from the sorted array has to take in account the time to shift the internal array indices. Here the sorted set has a far better average performance.
| TestCase | + | ? | - | -A | Z- |
|---|---|---|---|---|---|
| 5000 Items | |||||
| SortedSet | 69 | 1 | 7 | 4 | 4 |
| SortedArray | 53 | 47 | 51 | 6 | 0 |
| 10000 Items | |||||
| SortedSet | 149 | 1 | 17 | 10 | 9 |
| SortedArray | 122 | 105 | 116 | 25 | 0 |
| 50000 Items | |||||
| SortedSet | 908 | 7 | 118 | 56 | 50 |
| SortedArray | 1015 | 628 | 1000 | 782 | 1 |
AS3Commons SortedSet tests source code
AS3Commons Set tests source code
AS3Commons Treap tests source code
AS3Commons SortedList tests source code
Summary
There is more than just arrays
Array and Dictionary are not the only possibilities to realise data containers in ActionScript. Choosing an alternative to Array and Dictionary is often valuable for the reasons of performance, functionality and reliability.
This article has shown several examples where the the built-in containers do not suffice and what implementations can better solve specific problems. An array wrapper might be used to control the array access and to add functionality, the linked list has a better performance of insertion and removal operations than an array, sorted list or binary search tree are very fast containers to maintain a persistent sort order, a custom map can fix an unsatisfying behaviour of the Dictionary and add introspection, the different set implementations provide fast access by instance, the ordered map saves the insertion order while elements containted by a sorted map are kept in a sort order.
Decide reasonable
All introduced collections have specific benefits and disadvantages. It depends on the special software problem what data container fits the best. Usually we need only a small and specific subset of the entire functionality of a data container. It is worthy to spend some time to analyse the problem and depending on that to choose an appropriate container. The following list suggest a few questions that may be asked.
- Is any succession of elements required?
- Could a sorted structure be more performant?
- Is random access required (index, key, item by instance)?
- Yes: What access type could be the best?
- No: Are there lot of reads and writes in between a sequential container?
- Are duplicates permitted?
AS3Commons collaboration project
While in other languages the use of collections is highly popular, it isn’t in ActionScript. The reason might be a missing standard library. However, the algorithms used to implement collections are general, and the different ActionScript solutions we can find in the web are pretty similiar. Leads to the question, why we still do not have a unified collections framework in ActionScript
The AS3Commons project is a (yes, again another!) community approach to create a repository of reusable ActionScript components. I have contributed to the collections library there. If you have any feedback, you are welcome. Thank you.
AS3Commons collaboration project
AS3Commons Collections homepage

RSS




11 Comments
AS3Commons Collections framework - Flashforum
[...] [...]
Source Codes
This is a very interesting article with a lot of info to chew on.
Great job
Kevin
This is totally what i want, Thanks a lot.
Mehdi
Nice article. I wish you included library-size as another parameter in your comparison (Obviously I mean the additional K.bytes any given library would add to the overall size of the resulting SWF/Fla).
Thanks.
EventListener Manager (ELM) 1.20 - Flashforum
[...] [...]
Shripad N. Joshi
hey, it’s quite useful to me.
thanks a lot…
Experimental Videogames » StringSet Class for Actionscript 3
[...] implemented using Dictionary, which is pretty fast when asking for [...]
Surendra Gurjar
Nice explanation……
Array, Dictionary, Collections – Performance, Functionality, Reliability « while(thoughts){ "i am writing"}
[...] Array, Dictionary, Collections – Performance, Functionality, Reliability Share this:EmailPrintTwitterDiggStumbleUponFacebookRedditLike this:LikeBe the first to like this. [...]
4s
Very good blog! Do you have any recommendations
for aspiring writers? I’m planning to start my own site soon but I’m a little lost
on everything. Would you suggest starting with a free platform like WordPress or go for a paid option?
There are so many choices out there that I’m completely confused .. Any tips? Bless you!
Xiao
What’s about Vector in ActionScript ?