Java: конечная древовидная карта полей изменяет размер

#java

#java

Вопрос:

У меня есть Java-код, который содержит класс TRADE_HISTORY, который хранит историю сделок.

Класс TRADE_HISTORY имеет конечное поле с именем fMapDateOutputPriceRatios, которое задается в конструкторе . fMapDateOutputPriceRatios — это сопоставление дат и двойного массива (TreeMap). В конструкторе поле сравнивается с аргументом с помощью

 fMapDateOutputPriceRatios = new TreeMap<Date, double[]>(aOutputPriceRatioData); 
  

Количество дат получается с использованием

 Set<Date> dates = fMapDateOutputPriceRatios.keySet();
  

Размер дат распечатывается в конструкторе. Класс имеет только один конструктор.

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

  double[] outputPriceRatios = fMapDateOutputPriceRatios.get( aDate );
  

Ошибка возникает из-за того, что дата недоступна.

При попытке отладки ошибки печатается размер дат.

Во время построения размер составляет 1973 элемента.

При возникновении ошибки размер составляет 1964 элемента.

В частности, дата 11 апреля 2011 недоступна на момент возникновения ошибки.

Я использую eclipse и установил разрыв в переменной fMapDateOutputPriceRatios на разрыв при изменении поля. Она прерывается только во время конструктора.

Есть предложения о том, как определить, почему изменяется размер fMapDateOutputPriceRatios?

Единственными строками, которые обращаются к fMapDateOutputPriceRatios, являются

 TRADE_HISTORY::TRADE_HISTORY(Map<Date, double[]> aOutputPriceRatioData )
        fMapDateOutputPriceRatios = new TreeMap<Date, double[]>(aOutputPriceRatioData); 
        Set<Date> dates = fMapDateOutputPriceRatios.keySet();  // Used to debug error

TRADE_HISTORY::public void addTradeDistribution_0_to_100(Date aDate, ...)
        outputPriceRatios = fMapDateOutputPriceRatios.get( aDate )  // Causes error
        Set<Date> dates = fMapDateOutputPriceRatios.keySet();   // Used to debug error
  

Комментарии:

1. Было бы полезно видеть соответствующий код в одном связном блоке вместо неформатированного и перемежающегося описательным текстом.

2. Код довольно длинный, но я согласен, что дополнительная информация была бы полезной.

Ответ №1:

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

Обратите внимание, что набор ключей, возвращаемый keySet() , поддерживается картой. Если вы удалите из нее ключи, соответствующие сопоставления будут удалены из fMapDateOutputPriceRatios . Вы модифицируете dates или используете ее для чего-либо другого, кроме отладки?

Комментарии:

1. Изменение dates набора тоже было моей первой мыслью. Судя по тому, как представлен код, я чувствую кого-то, кто обычно пишет C , а не Java. final в Java ЭТО НЕ ТО ЖЕ самое, что const в C .

2. В карте ключей изменений не было.

3. Передача Map в конструктор копирует сопоставления, поэтому оригинал не повлияет на него после построения. Единственными другими способами удаления элементов являются remove() операции and над коллекциями, возвращаемыми keySet() , entrySet() и values() . Вы должны где-то это делать.

Ответ №2:

Во-первых, важный вопрос: вы уверены, что даты, используемые в качестве ключей, нигде не изменены? Предполагается, что Date является неизменяемым объектом, но он по-прежнему содержит устаревшие методы, позволяющие изменять содержимое ключа. Это привело бы к непредсказуемым последствиям для TreeMap, включая те, которые вы описали.

Кроме того, убедитесь, что все даты имеют часы / минуты / секунды / миллисекунды, очищенные до 0.

Теперь предположим, что даты неизменяемы:

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

TreeMap приходится перестраивать дерево при добавлении нового ключа. То есть отсоедините часть дерева и повторно свяжите его где-нибудь в другом месте дерева. В течение этого времени, если другой поток обращается к той же структуре и делает то же самое, это поддерево может быть потеряно, что приведет к уменьшению количества ключей.

Поэтому в качестве первого шага попробуйте получить доступ к этому полю в синхронизированном блоке:

 synchronized(fMapDateOutputPriceRatios) {
    outputPriceRatios = fMapDateOutputPriceRatios.get( aDate )
}
  

P.S. На самом деле я нигде не вижу put() в вашем коде, но он должен быть там, чудес не бывает.

Комментарии:

1. Операционная система передает существующий файл Map в TreeMap конструктор. put() нет необходимости заполнять карту.

2. В какой-то момент я планирую использовать несколько потоков, но сейчас должен быть запущен один поток. Вот код из более высокого класса. code логическое значение useThreads = false; логическое значение useThreadService = true; if (useThreads) { Runnable worker = новый fooWorker(текущая конфигурация, fPortfolio, fFileDirectory); if (useThreadService) { executor.execute(рабочий); } else { worker.run(); } } else { fooWorker worker = новый fooWorker (текущая конфигурация, fPortfolio, fFileDirectory); worker.run(); } code

3. В классе driver конструктору передаются соотношения даты и цены для ввода и вывода и устанавливается общая дата, если она создана. В функции обработки класса driver создается экземпляр TRADE_HISTORY и передается карта выходных данных / цен. Затем в функции обработки выполняется цикл по общим датам и создается новая сделка.

4. В подпрограмме драйвера выполняется цикл по датам, которые являются общими для входных и выходных карт даты / цены. В момент возникновения ошибки в TRADE_HISTORY добавляется дата, и эта дата не существует в поле отображения даты / цены в TRADE_HISTORY. Поле map было инициализировано во время построения TRADE_HISTORY. Однако дата должна быть там, и дата действительно существует на карте, которая присутствует в классе драйвера, поэтому код был изменен, чтобы удалить карту даты / цены из класса TRADE_HISTORY и использовать карту, которая есть в коде драйвера, программа запускается.

5. Если я изменю код обратно, на отображение даты / цены в TRADE_HISTORY, и помещу доступ к карте в синхронизированный блок, как предложено, тогда программа завершится, но ошибки или сообщения не будет, только два оператора null. В этом случае я не знаю, как определить причину. Я запускаю debug в eclipse, и обычно это останавливается на причине ошибки во время выполнения.

Ответ №3:

Спасибо за отзыв.

Перемещение карты даты / выходных цен в управляющую функцию устраняет возможность проблемы с картами даты / выходных цен. Тем не менее, по-прежнему возникают непредвиденные ошибки.

В конструктор TRADE_HISTORY был добавлен статический счетчик для отслеживания количества созданных TRADE_HISTORY. Кроме того, в конструктор был добавлен целочисленный идентификатор, равный счетчику, поэтому идентификаторы должны быть 1, 2, 3….

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

Если потребуется дополнительная помощь, будет запущен другой вопрос.