Create an Iterator for the ArrayBag class. The iterator must be in aprivateand non-static class named BagIterator. It must implement all Iterator methods, including theremove()method.Ensure that the Iterator returned by ArrayBag's
iteratormethod is an object of type BagIterator. Make sure it throws all the proper exceptions:
- NoSuchElementException if
next()is called when there is no next element.- IllegalStateException if
remove()is called twice in a row.- IllegalStateException if
remove()is called beforenext().- ConcurrentModificationException if the Bag has had any elements added or removed (unless they've been removed by this Iterator).
Other than activating method calls in TestIterator, all changes are to be made in ArrayBag. If you make changes anywhere else your code will not work with the automated testing system.
It'd be best if you were to complete this assignment in parts.
Revise ArrayBag
by implementing the iterator method.
The implementation requires you to make a private class
named BagIterator
that implements Iterator<T>
inside the ArrayBag class.
Do not make the private classstatic. By making it an "instance class" it can refer to elements of the ArrayBag that created it. In particular, it can talk aboutnumInBagandcontentswithout needing to specify which ArrayBag they're part of. They automatically refer to the fields of the correct ArrayBag.Also, because it is an instance class, it has access to the type parameter T, so you don't need to re-introduce that parameter.
A BagIterator is an object that lets a client iterate thru the elements of this Bag. The BagIterator class has the following methods:
next(),
which returns the next item from this Bag
that hasn't already been returned by next().
hasNext(),
which checks whether there are any items in the Bag
that haven't already been returned by the next() method.
remove(),
which removes the last item returned by next().
A BagIterator is used like any other Iterator:
Do the basic implementation of the
hasNext() and next() methods.
Don't worry yet about throwing exceptions.
Run the program TestIterator program to ensure that your iterator loop works. If you've done everything correctly, you should see something like this:
It is possible that the items in the Bag will be reported in a different order. For example:
It doesn't matter what order the words appear. All that's important is that all and only the words in the Bag appear, with no extra duplicates.
Those of you who understand about ADTs may be saying "Hey! The two iterators have different behaviour. Shouldn't the behaviour be the same for both?"That is a very good point.
Nevertheless, it's fine right now because later we're going to make it so that neither of those messages get printed.
next method
so that it throws a NoSuchElementException
if all of there are no more items to return.
Once you have completed the implementation, your output should look like this:
Or possibly like this:
remove method to the BagIterator.
This method uses ArrayBag's removeItemAt method
to remove the element returned by the previous call to next().
Note that the removeItemAt method
rearranges the elements of the array.
It's important that items not removed by the iterator
still get seen by the iterator.
For example,
if the ArrayBag's contents array is [10, 20, 30, 42, 50, null]
and the previous call to next returned the 20,
then remove()
will update the contents array to [10, 50, 30, 42, null, null].
If the 50 hasn't already been seen,
then the next call to next() needs to return the 50.
Of course,
if the 50 has already been seen,
then next() should not return it.
It's also important that items not get seen mutiple times.
Activate the testPart3 method call
in TestIterator
and run the program again to ensure that you get this behaviour.
In addition to the output shown for parts 1 and 2,
you should now see:
Or this:
remove
throws an IllegalStateException
whenever it's called at a bad time.
It's only OK to call remove
when next has been called
since the previous (if any) call to remove.
Add a boolean property to your BagIterator
to track whether it's OK to call remove:
remove.
next gets called successfully
(that is, without throwing an exception),
it becomes OK again to call remove.
remove get called successfully
(again, without throwing an exception),
it becomes not OK to call it again.
Activate the call to testPart4 in TestIterator
and run it.
You should see the following additional output:
It is possible the two numbers remaining are 20.2 and 10.1 instead of 30.3 and 20.2.
removeAll
and
retainAll
methods
in ArrayBag
using a BagIterator.
Having access to a BagIterator
allows you to simply iterate thru your bag
and use the iterator's remove method
to delete items as appropriate.
When you're done,
activate the call to testPart5.
In addition to what you saw above,
you should now see:
It's fine if the Bag's elements are in a different order. For example:
hasNext, next and remove
throw ConcurrentModificationExceptions
when they notice a change to the ArrayBag
that this Iterator didn't make.
The way to do this is to keep track of how many additions and removals get made to the ArrayBag.
First, add an instance variable to ArrayBag for the number of updates made. It should start at zero, and get bumped up by one every time an addition or removal is made.
Find the places in ArrayBag
where numItems gets changed.
Those are the places where the number of updates
needs to be incremented.
Second, the BagIterator needs to make a note of how many updates the ArrayBag had when it (the BagIterator) was created. Create an instance variable for that. This is how many updates the iterator expects to see.
Every time the BagIterator's remove method
actually removes an element from the ArrayBag,
it needs to increment its expected number of updates.
(The ArrayBag will notice the call to removeItemAt
and increment its count of updates.
If BagIterator doesn't also increment,
the expected and actual numbers will come apart
even tho' the update was made by this iterator.)
Lastly, before the public iterator methods do anything else, they need to check whether the actual number of updates (as recorded in the ArrayBag) and the expected number of updates (as recorded in the BagIterator) are equal. If they are not equal, then the method needs to throw a ConcurrentModificationException.
Since both methods need to do the same check
and respond in the same way,
it's best to create a private method
to do the check and throw the exception as appropriate.
Activate the testPart6 call in TestIterator
and check the output.
There should be a change in the output
for Parts 1 & 2:
Again, it's possible the words come out in a different order, and that you get different numbers in part 6. For example:
For those of you who noticed the difference outputs
in parts 1 and 2,
you should now notice that the two versions
have replaced their distinct messages
(OK, now there is a "extended" in the bag
and
Didn't notice the extra thing in the bag)
with the common message
Good! You noticed that change!
The testing program was adding a word to the bag and then asking the iterator if it could see it. One version could, the other could not. But the correct response for the iterator is to cry out "Hey! Someone's been messing with my Collection!"