Collections

Collections are Java classes developed to efficiently store multiple elements of a certain type. Unlike Java arrays collections can store any number of elements. The simplest collection is ArrayList, which you can treat as a resizable array. The following line of code creates an (initially empty) ArrayList of objects of class Person:

ArrayList<Person> friends = new ArrayList<Person>();

Note that the type of the collection includes the element type in angle brackets. This "tunes" the collection to work with the specific element type, so that, for example, the function get() of friends will return object of type Person. The ArrayList<Person> offers the following API (for the complete API see Java Class Reference):

The following code fragment tests if the friends list contains the person victor and, if, victor is not in there, adds him to the list:

if( ! friends.contains( victor ) )
    friends.add( victor );

All collection types support iteration over elements. The simplest construct for iteration is a "for" loop. Suppose the class Person has a field income. The loop below prints all friends with income greater than 100000 to the model log:

for( Person p : friends ) {
    if( p.income > 100000 )
        traceln( p );
}

Another popular collection type is LinkedList

Linked lists are used to model stack or queue structures, i.e. sequential storages where elements are primarily added and removed from one or both ends.

Consider a model of a distributor that maintains a backlog of orders from retailers. Suppose there is an Order class with the amount field. The backlog (essentially a FIFO queue) can be modeled as:

LinkedList<Order> backlog = new LinkedList<Order>();

LinkedList supports functions common to all collections (like size() or isEmpty()) and also offers a specific API:

When a new order is received by the distributor it is placed at the end of the backlog:

backlog.addLast( order );

Each time the inventory gets replenished, the distributor tries to ship the orders starting from the head of the backlog. If the amount in an order is bigger than the remaining inventory, the order processing stops. The order processing can look like this:

while( ! backlog.isEmpty() ) { //repeat the code below while the backlog is not empty
    Order order = backlog.getFirst(); //pick the first order in the backlog
    if( order.amount <= inventory ) { //if enough inventory to satisfy this order
        ship( order ); //ship
        inventory -= order.amount; //decrease available inventory
        backlog.removeFirst(); //remove the order from the backlog
    } else { //not enough inventory to ship
        break; //stop order backlog processing
    }
}

We recommend to declare collections in agents and experiments graphically. The Collection object is located in the Agent palette. All you need to do is to drop it on the canvas and choose the collection and the element types. At runtime you will be able to view the collection contents by clicking its icon.

Declaring Java collection graphically

Different types of collections have different time complexity of operations. For example, checking if a collection of 10,000,000 objects contains a given object may take 80 milliseconds for ArrayList, 100 milliseconds for LinkedList, and less than 1 millisecond for HashSet and TreeSet. To ensure maximum efficiency of the model execution you should analyze, which operations are most frequent and choose the collection type correspondingly. The Table below contains the time complexities of the most common operations for four collection types. 

Operation

ArrayList

LinkedList

HashSet

TreeSet

Obtain size

Constant

Constant

Constant

Constant

Add element

Constant

Constant

Constant

Log

Remove given element

Linear

Linear

Constant

Log

Remove by index

Linear

Linear

Get element by index

Constant

Linear

Find out if contains

Linear

Linear

Constant

Log

The terms constant, linear, and logarithmic complexity mean the following. Linear complexity means that the worst time required to complete the operation grows linearly as the size of the collection grows. Constant means that it does not depend on the size at all, and Log means the time grows as the logarithm of the size. You should not treat the constant complexity as the unconditionally best choice. Depending on the size, different types of collection may behave better than others. 

Consider the Figure. It may well happen that with relatively small number of elements the collection with linear complexity will behave better than the one with constant complexity.

Depending on the size, different types of collections may behave better that others

And a couple of things you should keep in mind when choosing the collection type:

Agent populations are collections too

When you declare an embedded agent as replicated, AnyLogic creates a special type of collection to store the individual agents. You have two options: 

The options for the type of collection appear in the Advanced section of the embedded object properties, see the Figure. 


Options for collection type of a replicated object

Both collections support functions like size(), isEmpty(), get( int index ), contains( Object ao ), and iteration. If index is not specifically needed during iteration, it is always better to use the enhanced form of "for" loop:

for( Person p : people ) {
    …
}