Коллекции

Коллекции представляют собой Java классы, предназначенные для эффективного хранения нескольких элементов определенных типов. В отличие от массивов Java, коллекции могут содержать любое число элементов. Простейшей коллекцией является ArrayList; ее можно рассматривать как массив с изменяемым размером. Следующая строка кода создает изначально пустой список ArrayList объектов класса Person:

ArrayList friends = new ArrayList();

Обратите внимание, что тип коллекции содержит тип элемента, заключенный в угловые скобки. Таким образом коллекция "настраивается" для работы с определенным типом элементов, вследствие чего, например, функция get() объекта friends вернет объект типа Person. Программный интерфейс класса коллекции ArrayList включает следующие функции (полный набор функций содержится в Справочнике классов Java):

Следующий фрагмент кода проверяет, содержит ли список friends элемент victor, и если нет, то добавляет его в список:

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

Все типы коллекций поддерживают итерирование по элементам. Самой простой конструкцией для итерации является цикл for. Предположим, что у класса Person есть поле income. Приведенный ниже цикл распечатывает всех друзей с доходами больше 100000 в лог модели:

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

Другой популярный тип коллекций - связный список LinkedList.

Связные списки используются для моделирования очередей или стеков, то есть, последовательных хранилищ, в которые элементы добавляются и удаляются главным образом с одного или обоих концов.

Рассмотрим модель, в которой дистрибьютор хранит журнал невыполненных заказов, полученных от ритейлеров. Предположим, есть класс Order с полем amount. Тогда журнал (являющийся. по сути очередью FIFO ("первым пришёл - первым обслужен")) может быть промоделирован как:

LinkedList backlog = new LinkedList();

Класс LinkedList поддерживает общие для всех коллекций функции (такие, как size() или isEmpty()), а также предлагает дополнительный набор методов:

Когда дистрибьютор получает новый заказ, он помещает его в конец списка невыполненных заказов:

backlog.addLast( order );

Каждый раз, когда запасы товаров пополняются, дистрибьютор пытается отправить товары согласно заказу, находящемуся в начале журнала. Если значение в заказе превышает имеющиеся в наличии товары, то обработка заказа прекращается. Обработка может выглядеть так:

while( ! backlog.isEmpty() ) { //повторяем код ниже, пока журнал не пуст
Order order = backlog.getFirst(); //берем первый заказ из журнала
if( order.amount <= inventory ) { //если достаточно товара, чтобы выполнить заказ
ship( order ); //выполняем заказ
inventory -= order.amount; //уменьшаем количество товара на складе
backlog.removeFirst(); //удаляем заказ из журнала
} else { //если недостаточно товара,
break; //прекращаем обработку журнала заказов
}
}

Мы советуем задавать коллекции в агентах и экспериментах графически. Элемент Коллекция расположен в Основной палитре. Всё, что вам нужно сделать - это перетащить его из палитры на диаграмму и выбрать тип коллекции и тип элементов. Во время выполнения модели вы сможете просмотреть содержимое коллекции, щелкнув по её значку.


Графическое задание коллекции Java

Различные типы коллекций имеют разную временную сложность операций. Например, проверка наличия заданного объекта в коллекции, содержащей 10 000 000 объектов, может потребовать 80 миллисекунд для коллекции типа ArrayList, 100 миллисекунд – для LinkedList, и менее 1 миллисекунды – для HashSet и TreeSet. Для обеспечения наибольшей эффективности необходимо исследовать, какие из операций наиболее часто выполняются, и выбрать соответствующий тип коллекции. В следующей таблице приведена временная сложность наиболее часто используемых операций для четырех типов коллекций.

Операция

ArrayList

LinkedList

HashSet

TreeSet

Получение размера

Постоянная

Постоянная

Постоянная

Постоянная

Добавление элемента

Постоянная

Постоянная

Постоянная

Логарифмическая

Удаление данного элемента

Линейная

Линейная

Постоянная

Логарифмическая

Удаление по индексу

Линейная

Линейная

Получение элемента по индексу

Постоянная

Линейная

Определение присутствия элемента в коллекции

Линейная

Линейная

Постоянная

Логарифмическая

Термины постоянная, линейная и логарифмическая сложность означают следующее: "линейная" - что наихудший показатель времени, необходимого для успешного завершения операции, линейно зависят от размера коллекции; "постоянная" означает, что зависимость от размера отсутствует; "логарифмическая" - что время находится в логарифмической зависимости от размера.

Постоянная сложность не должна рассматриваться как безоговорочно лучший выбор. В зависимости от размера, одни типы коллекций могут проявлять себя лучше других.

Обратите внимание на приведенный ниже рисунок. Вполне может быть, что при относительно малом числе элементов коллекция с линейной сложностью будет работать лучше, чем коллекция с постоянной сложностью.


В зависимости от размера, одни типы коллекций могут вести себя лучше других

И пара моментов, которые следует знать при выборе типа коллекции:

Популяции агентов также являются коллекциями

Когда вы объявляете вложенный агент реплицированным, AnyLogic создает специальный тип коллекции для хранения отдельных агентов. Вы можете выбрать один из двух следующих типов:

Тип коллекции можно выбрать в секции свойств Специфические вложенного агента, см. рисунок ниже.


Выбор типа коллекции популяции агентов

Обе коллекции поддерживают такие функции, как size(), isEmpty(), get( int index ), contains( Object ao )и итерацию по элементам. Если во время итерации не нужно оперировать со значением текущего индекса, то всегда лучше будет использовать упрощенную форму цикла for:

for( Person p : people ) {

}