J Vorwald
J Vorwald

Reputation: 7

Java: final field treemap changes size

I have a java code that contains a class TRADE_HISTORY that holds a history of trades.

Class TRADE_HISTORY has a final field named fMapDateOutputPriceRatios that is set in the constructor . fMapDateOutputPriceRatios is a map between dates and a double array (TreeMap). In the constructor, the field is assinged to the argurment using

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

The number of dates is obtained using

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

The size of dates is printed out in the constructor. The class has only one constructor.

A problem is occurring when a new trade is added. When a new date is added, the double vector is used, and obtained from

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

The error occurs because the date is not available.

While trying to debug the error, the size of dates is being printed.

During construction, the size is 1973 elements.

When the error occurs, the size is 1964 elements.

In particular, the date Apr 11, 2011 is not available at the time of the error.

I'm using eclipse and have set a break on the variable fMapDateOutputPriceRatios to break when the field is modified. It only breaks during the constructor.

Any suggestion on how to determine why the size of fMapDateOutputPriceRatios changes?

The only lines that access fMapDateOutputPriceRatios are

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

Upvotes: 0

Views: 319

Answers (3)

J Vorwald
J Vorwald

Reputation: 7

Thank you for the feedback.

Moving the date/output price map to the driving function removes the possibility of a problem with date/output price maps. However, still get unexpected errors.

A static counter was added to the constructor of TRADE_HISTORY to keep track of the number of TRADE_HISTORY constructed. Also, an integer id was added to the constructor and set equal to the counter, so the id's should be 1, 2, 3....

When the current error occurs, the TRADE_HISTORY id is printed and is zero, which should not occur. More debugging is needed in the constructor of TRADE_HISTORY instances. It appears that there is a TRADE_HISTORY being used that was not constructed properly.

If more help is required, a different question will be started.

Upvotes: 0

David Harkness
David Harkness

Reputation: 36542

A final reference to an object instance does not make that instance immutable! It only blocks changing the reference to point to a different object instance. The reference is final--not the state of the object instance it references.

Note that the set of keys returned by keySet() is backed by the map. If you remove keys from it, the corresponding mappings are removed from fMapDateOutputPriceRatios. Are you modifying dates or using it for anything other than debugging?

Upvotes: 3

Vladimir Dyuzhev
Vladimir Dyuzhev

Reputation: 18336

First, important question: are you sure Dates used as keys are not modified anywhere? Date is supposed to be an immutable object, but it still contains legacy deprecated methods letting one to change the key content. That would cause unpredictable consequences to TreeMap, including the ones you described.

Also, make sure all Dates has hours/minutes/seconds/milliseconds cleaned to 0.

Now, suppose the Dates are immutable:

You have actually observed the reduction of the key set size, which means the physical modification of the content happens. The only way I know how it can happen is a concurrent access to the map by two or more threads.

TreeMap have to rebuild the tree when new key is added. That is, unlink part of the tree, and re-link it somewhere else in the tree. During that time, if another thread accesses the same structure and does the same, that subtree may get lost, causing reduction in number of keys.

Therefore, as a first step, try to access this field in a synchronized block:

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

P.S. I actually do not see put() in your code anywhere, but it must be there, there are no miracles.

Upvotes: 0

Related Questions