Question
This file is complete, however, it's an able-to-be-improved ArrayMultiSet. The class currently uses an Iterator, but one that is prone to errors. Update the ArrayMultiSet
This file is complete, however, it's an "able-to-be-improved" ArrayMultiSet.
The class currently uses an Iterator, but one that is prone to errors. Update the ArrayMultiSet class AND/OR its inner-class Iterator so that it has a working fail-fast Iterator.
This is the file to be worked on:
import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; /** * Class which implements the concept of a multiset -- an unorganized collection which does not limited the number of * times an instance can be added. * * @paramType of data contained with the instance. */ public class ArrayMultiSet implements Collection { /** Array in which the elements in this multiset are stored. */ private E[] _store; /** * Array indices below this amount contain the active elements in this collection. */ private int _size; /** * Modification counter used to preserve the fail-fast nature of its iterators. */ private long _modCount; /** * Create a new empty multiset. */ public ArrayMultiSet() { _modCount = 0; clear(); } /** * Remove all of the elements within the instance and invalidate any current iterators. */ @SuppressWarnings("unchecked") @Override public void clear() { _store = (E[]) (new Object[16]); _size = 0; // maintains the class invariant } /** * Update the multiset so that it includes all of the elements from before the call AND the given element. * * @param e Item to be added to this collection. */ @Override public boolean add(E e) { // Check if we do not have enough space in the underlying array to store the // new element if (_size == _store.length) { // We do not have space, so create a new larger space (doubling the size // is the most time efficient) _store = Arrays.copyOf(_store, _store.length * 2); } // Add the element to the store _store[_size] = e; // Finally, we can increase _size, since this change will no longer violate // any class invariants. _size += 1; return true; } /** * Return true if at least one element in the multiset is equal to the given object. When {@code obj} is null, it must * use the {@code ==} operator to perform these checks, but when {@code obj} is not null, the {@link Object#equals} * method is used. * * @param obj Object (or null) for which we will search * @return {@code true} if {@code obj} was found; {@code false} if a match could not be found. */ @Override public boolean contains(Object obj) { // Only scan through _size, since those are the only "real" entries for the // multiset. for (int i = 0; i < _size; i++ ) { // When obj is null, we need to use == if ((obj == null) && (_store[i] == null)) { return true; } // Otherwise, we use .equals() to find a match else if ((obj != null) && obj.equals(_store[i])) { return true; } // No else clause, since the match could be at a higher index! } // Checked all VALID indices, so the result must be: return false; } @Override public int size() { return _size; } /** * Remove the element found at the given index. This method also acts to maintain the class invariants.
* Precondition: {@code i} is a valid index within {@code _store}. * * @param i Index of the element to remove from the multiset. */ private void removeAtIndex(int i) { // We do not need to check i, since we made that a precondition (assumption) // for this method. // Slide elements at highest index down to fill in "hole" we are creating _store[i] = _store[_size - 1]; // Update which is the first unused index in the array _size -= 1; // Finally set the newly unused index to null thus avoiding a space leak _store[_size] = null; } /** * Removes a single instance of the given object, if one can be found in the multiset. The method returns {@code true} * if a match was found (and removed) and {@code false} if no match was found. Normally, this uses * {@link Object#equals} to check if there is a match, but uses {@code ==} when {@code obj} is {@code null}. * * @param obj Object (or null) which we want to remove * @return {@code true} if {@code obj} was found and an instance removed; {@code false} if a match could not be found. */ @Override public boolean remove(Object obj) { for (int i = 0; i < _size; i++ ) { if ((obj == null) && (_store[i] == null)) { removeAtIndex(i); return true; } else if ((obj != null) && obj.equals(_store[i])) { removeAtIndex(i); return true; } // No else clause, since a match may still exist at a higher index! } // Checked all VALID indices, so the result must be: return false; } @Override public boolean isEmpty() { return _size == 0; } @Override public Iteratoriterator() { return new MultiSetIterator(); } /** * Instances of this class are used as the iterators for a multiset. We must keep this as an inner-class, because the * multiset definition does not include any methods to access its elements. * * @author Carl Alphonse * @author Matthew Hertz */ private class MultiSetIterator implements Iterator { /** * The index of the _store entry which will be returned by the next call to next() */ private int _cursor; /** * In keeping with the fail-fast convention, the iterator is only valid while _modCount remains on this version. */ private long _collectionVersion; /** * Create a new instance of this class that will go through the (valid) entries in the store. */ public MultiSetIterator() { _cursor = 0; } public boolean hasNext() { } public E next() { } public void remove() { throw new UnsupportedOperationException(); } } /* * The remaining methods are part of the Collection interface, but are beyond what is necessary for CSE 116. * Students who want a complete Multiset implementation should investigate Google's "Guava" library. */ @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public T[] toArray(T[] a) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection> c) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection extends E> c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection> c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection> c) { throw new UnsupportedOperationException(); } }
VERIFY YOUR ANSWER BY USING THE TEST CASE PROVIDED BELOW:
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.*;
import java.lang.reflect.Field; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException;
import org.junit.Before; import org.junit.Test;
public class ArrayMultiSetIteratorTest {
@Test public void checkIteratorVersioning() throws IllegalAccessException { ArrayMultiSet
collectionVersion = getCollectionVersion(testeeTwo); assertEquals("Iterator constructor should assign _collectionVersion the correct value.", 72, collectionVersion);
}
@Test public void checkHasNextStart() throws IllegalAccessException { ArrayMultiSet
@Test public void checkHasNextMiddle() throws IllegalAccessException { ArrayMultiSet
@Test public void checkHasNextLast() throws IllegalAccessException { ArrayMultiSet
@Test public void checkHasNextAfterLast() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextStartNotEmpty() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextMiddle() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextLast() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextAfterLast() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextModWhileInCollectionMiddle() throws IllegalAccessException { ArrayMultiSet
@Test public void checkNextModAfterCollectionEnd() throws IllegalAccessException { ArrayMultiSet
@Test public void checkAddUpdatesModCount() throws IllegalAccessException { ArrayMultiSet
@Test public void checkRemoveSuccessUpdatesModCount() throws IllegalAccessException { ArrayMultiSet
@Test public void checkRemoveFailsModCountUnchanged() throws IllegalAccessException { ArrayMultiSet
private Field cursorField; private Field collectionVersionField; private Field modCountField;
@Before public final void checkFieldsUnchanged() { ArrayMultiSet> ams = new ArrayMultiSet<>(); Class> amsClass = ams.getClass(); try { modCountField = amsClass.getDeclaredField("_modCount"); modCountField.setAccessible(true); } catch (Exception e) { fail("Your ArrayMultiSet class should still define a field named \"_modCount\""); } Class> cIterator = ams.iterator().getClass(); Field[] fields = cIterator.getDeclaredFields(); assertEquals("You should not add any fields to the MultiSetIterator class. This class's field count:", 3, fields.length); try { collectionVersionField = cIterator.getDeclaredField("_collectionVersion"); collectionVersionField.setAccessible(true); } catch (Exception e) { fail("Your MultiSetIterator class should still define a field named \"_collectionVersion\""); } try { cursorField = cIterator.getDeclaredField("_cursor"); cursorField.setAccessible(true); } catch (Exception e) { fail("Your MultiSetIterator class should still define a field named \"_cursor\""); } }
private long getModCount(ArrayMultiSet> testee) throws IllegalAccessException { return modCountField.getLong(testee); }
private void setModCount(ArrayMultiSet> testee, long newVersion) throws IllegalAccessException { modCountField.setLong(testee, newVersion); }
private long getCollectionVersion(Iterator> testee) throws IllegalAccessException { return collectionVersionField.getLong(testee); }
private void setCollectionVersion(Iterator> testee, long newVersion) throws IllegalAccessException { collectionVersionField.setLong(testee, newVersion); }
private int getCursor(Iterator> testee) throws IllegalAccessException { return cursorField.getInt(testee); }
private void setCursor(Iterator> testee, int newSize) throws IllegalAccessException { cursorField.setInt(testee, newSize); } }
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started