Solution

After a few days of driving myself insane trying to figure out the cause, I went to see Dr. Conrad and within an hour we had fixed the problem. Without going too deeply into how my code functions, all time intervals are stored as TimeInterval objects, and each day has a list of TimeInterval objects that represent that day's free times. The initial assumption was that somehow several lists were being called at once when only one was supposed to be called, hence causing the change to affect all the lists. However, this didn't explain why the change only affected days which had the same free time interval set to them. We created some JUnit tests to check whether all lists that had the same free time interval were actually pointing to the same object, but that did not seem to be the cause. Eventually, we decided to look at the part of my code that set the free time intervals initially, since that might explain why the malfunction only affected days that set the same free time. Once we started looking there, Dr. Conrad was able to figure out the problem, and within a few seconds everything was fixed. The problem was that I was esentially using a shallow copy to enter the initial free time intervals, when I really needed a deep copy. Here's an excerpt from my code that shows the problem:

//Create a new TimeInterval with the correct start and end times interval = new TimeInterval(startTime, endTime); for (int i = 0; i < days.length(); i++) { switch (days.charAt(i)) { case 'M': addFreeTimesToList(interval, MList); break; case 'T': addFreeTimesToList(interval, TList); break; case 'W': addFreeTimesToList(interval, WList); break; case 'R': addFreeTimesToList(interval, RList); break; case 'F': addFreeTimesToList(interval, FList); break; default: break; } } public void addFreeTimesToList(TimeInterval interval, List dayList) throws FreeTimesAlreadySetException { if (dayList.isEmpty()) dayList.add(interval);

The problem here is subtle. I am passing in the same physical TimeInterval object that represents the free time to every appropriate list, and adding that same copy of the object to each list. So in a way, every list that was being affected was the same because they all had the same copy of that interval in them. The reason the problem only appeared for the two specific cases mentioned above was due to some code later in my program:

//If interval begins before currInterval, but ends during it, //change currInterval's start time to interval's end time. else if (interval.isStartOut(currInterval)) { currInterval.setStart(interval.getEnd()); counter = numElements; } //If interval ends after currInterval, but starts during it, //change currInterval's end time to interval's start time. else if (interval.isEndOut(currInterval)) { currInterval.setEnd(interval.getStart()); counter++; }

Those two cases, represented by the code above, were the only two cases in which I actually alter the data members of the interval. So when I changed the data in the interval in one list, it affected all other lists that had the same copy of that interval. The solution to this problem was to copy the interval into a new object for each list it was added to, so that all 5 lists had different copies of the objects, regardless of whether they represented the same interval. There are two ways to accomplish this, both of which are shown below:

Solution 1: Create distinct objects before they're sent to addFreeTimesToList

for (int i = 0; i < days.length(); i++) { //Create a new TimeInterval with the correct start and end times interval = new TimeInterval(startTime, endTime); switch (days.charAt(i)) { case 'M': addFreeTimesToList(interval, MList); break; case 'T': addFreeTimesToList(interval, TList); break; case 'W': addFreeTimesToList(interval, WList); break; case 'R': addFreeTimesToList(interval, RList); break; case 'F': addFreeTimesToList(interval, FList); break; default: break; } }

Here all I did was to move the TimeInterval object creation inside the for loop, so that a new object was created for each new day.

Solution 2: Copy the interval object into a new object once it's been sent to addFreeTimesToList

public void addFreeTimesToList(TimeInterval interval, List dayList) throws FreeTimesAlreadySetException { TimeInterval newInterval = new TimeInterval(interval); if (dayList.isEmpty()) dayList.add(newInterval);

Here, I send the same copy of the object to every list in the call to addFreeTimesToList. However, inside that function, a new interval object is created from the old one, having the same data values. The new object is the one that is added to the list so every list has a different copy.

Either method works, and they both do essentially the same thing. My actual code uses Solution 1. Technically, Solution 2 creates one more copy of the object than Solution 1 because Solution 2 creates the initial copy that all other copies are made from. This isn't really an issue that needs to be worried about when deciding which method to use.

Back to the Problem


Anthony Michael Furst
Last modified: Fri May 5 21:21:37 EDT 2006