AnyLogic
Развернуть
Размер шрифта

Коллекции

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

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

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

Функция Описание
int size() Возвращает количество элементов в списке
boolean isEmpty() Проверяет данный список на отсутствие элементов
Person get(int index) Возвращает элемент, находящийся на указанной позиции в списке
boolean add( Person p ) Добавляет указанный элемент в конец списка
Person remove(int index) Удаляет элемент, находящийся на указанной позиции в списке
boolean contains( Person p ) Возвращает true, если данный элемент содержится в списке
void clear() Удаляет все элементы из списка

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

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

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

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

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

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

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

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

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

Функция Описание
Order getFirst() Возвращает первый элемент списка
Order getLast() Возвращает последний элемент списка
addFirst( Order o ) Вставляет заданный элемент в начало списка
addLast( Order o ) Добавляет заданный элемент в конец списка
Order removeFirst() Возвращает первый элемент списка и удаляет его из списка
Order removeLast() Возвращает последний элемент списка и удаляет его из списка

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

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
Получение размера Постоянная Постоянная Постоянная Постоянная
Добавление элемента Постоянная Постоянная Постоянная Логарифмическая
Удаление данного элемента Линейная Линейная Постоянная Логарифмическая
Удаление по индексу Линейная Линейная - -
Получение элемента по индексу Постоянная Линейная - -
Определение присутствия элемента в коллекции Линейная Линейная Постоянная Логарифмическая

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

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

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

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

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

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

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

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

  • AgentArrayList  — этот тип коллекции выбирают, если набор агентов более-менее постоянен, или если вам будет нужно часто обращаться к конкретным элементам популяции.
  • AgentLinkedHashSet  — этот тип следует выбирать, если вы планируете часто добавлять новые агенты или удалять существующие. Например, если вы моделируете население города в течение достаточно долгого промежутка времени, так, что люди в вашей модели рождаются, умирают, уезжают из города, и т.д..

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

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

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

for( Person p : people ) {
  …
}
Как мы можем улучшить эту статью?