Inserting and Retrieving Items
Overview
In this part of the lab, we will add functionality to our MyArrayList class to insert and retrieve items from our data structure. We will also practice using unit tests to debug and validate our implementation.
Adding Items to MyArrayList
The first main operation we will work on programming is adding items to our data structure since we cannot do anything unless it is storing data. Given how array lists store data, we need to first create a private helper method that will make sure our add methods for inserting data always work.
private void resize Method
Recall that an array list can theoretically store a near infinite number of items (depending only on the amount of memory your computer has). However, we are using an array inside the array list to physically store that data, and arrays always have a fixed length. To avoid ever running into a situation where the size of the array list (i.e., the number of items actually in the collection) becomes larger than its length (i.e., the number of items it can physically store, given its underlying array), we need an ability to resize the underlying array to make it larger so it can store enough items.
To do so, create a new private void resize() method that takes no arguments and:
- Creates a new
T[]array whoselengthis double thelengthof thethis.itemsarray. - Copy all of the existing items from the
this.itemsarray to our new array created in Step 1. Make sure those items all keep the same order (aforloop over indices of the items is really helpful here). - Assign your new array to the
this.itemsinstance variable.
Hint
Since we need to create a new T[] array, we should follow the same process we used in the first constructor in Part 1, including adding the @SuppressWarnings(āuncheckedā) annotation to this method.
public void add(int index, T item) Method
Our next method public void add(int index, T item) will be responsible for adding a given T item into a desired index in the array list. This method should:
- First check whether
indexmakes sense given the current number of items in the array list. That is, theindexshould be a number in the range[0, this.size]inclusive (otherwise there would be a gap between the current items and the new one). If theindexis not in that range, we should throw anIndexOutOfBoundsExceptionwith an error message that makes sense ā something like (feel free to change the text):
throw new IndexOutOfBoundsException("Tried to add an item to a MyArrayList with too large of an index. Index:" + index + " but size is " + this.size);
- Next, we need to ensure that there is room for the new item by checking whether
this.size < this.items.length. If so, we can proceed to Step 3. Otherwise, we need to resize the underlying array by calling ourresize()method we created above. - If
index < this.size, then we need to move all of the items starting atindexthrough the end of the array up one index. - Save the
itemargument into theindexposition of thethis.itemsarray. - Increase the
sizecount by 1
public boolean add(T item) Method
Our second approach for adding items to the array list is the method public boolean add(T item) that always adds an item to the end of the list. Because we already did all of the necessary work in the previous method, our second add method can simply call the first add method with the appropriate index. Question: what is the appropriate index to pass into the first add method in order to add a new item to the current end of the array list?
The only other instruction needed in the second add method is to always return true since we successfully added the item to the array list.
Retrieving Items from MyArrayList
We will only have one method for retrieving items from the array list: public T get(int index). Recall that we created a stub of this method while testing our code so far at the end of Part 1. We should change this method to:
- First verify that the
indexargument is valid, given the current number of items in the array list. That is,indexshould be a value in the range[0, this.size - 1]. Ifindexis not in this range, once again throw anIndexOutOfBoundsExeceptionwith an appropriate error message. - Return the item at the given
indexin thethis.itemsarray.
Testing Our Progress
Now that we have more methods that work together implemented, we can once again test our progress. Rather than adding more code to our public static void main method, we will instead use unit tests like in the Warmup.
In MyArrayList.java, right click anywhere in the file, and select āSource Actionā from the right click menu. Then, select āGenerate Testsā. Again, use the default name proposed by the IDE of MyArrayListTest. Next, it will prompt you with what methods you want to create tests for. Select just the size method for now (we will manually add other tests later).
You should now see a new java file named MyArrayListTest.java that contains a class named MyArrayListTest with one empty test method. Following a similar process to what we used in Warmup Part 1, we will create all the unit tests that we need to debug any mistakes in our implementation.
Testing add(int index, T item) and get(int index)
Even though the IDE created an empty test testSize for our size method, we should start by testing our methods for inserting items into the MyArrayList, otherwise it is difficult to check whether the size method works for non-empty array lists.
In your MyArrayListTest.java file, create a new test called testAddGet by copying the following code:
@Test
public void testAddGet() {
// create a MyArrayList that stores Integers
MyArrayList<Integer> array = new MyArrayList<Integer>();
// add a couple items
// afterwards, the array should be [10, 11, 12]
array.add(0, 10);
array.add(1, 11);
array.add(2, 12);
// get the items back out of the MyArrayList
int item0 = array.get(0);
int item1 = array.get(1);
int item2 = array.get(2);
// check whether the items were in the correct order
assertEquals(10, item0);
assertEquals(11, item1);
assertEquals(12, item2);
}
This test verifies that:
- We can insert new items to the end of the list (first 10, then 11, then 12)
- The underlying array is resized when needed (when we try to add the third item)
- We can retrieve items back out of the list
- The items are inserted and retrieved in the correct order
Run this test to make sure everything works! If any line of the test does not pass, hopefully where it fails and what is stored in item0 and item1 will give you a hint of where to find any bugs in your implementation of MyArrayList. Donāt forget to use the Debugger from Part 2 of the Warmup to help you debug.
More Testing for add(int index, T item)
While the above test is useful for checking whether we can add an item to the end of a MyArrayList object, we also need to test whether we can correctly add to an index where there is already an item in the list. We split this into a second, separate unit test so that our unit tests do not get too complicated, but instead each test verifies a different use case of our data structure.
Create another test called testAddMiddle that:
- Creates a new
MyArrayListobject - Adds two
items to the array list - Adds a new unique
itemtoindex = 0of the array list - Gets the unique
itemfromindex = 0(assigned to a local variable as in our test above) and then asserts that the value of that local variable is equal to theitemadded in Step 3 - Adds a final unique
itemtoindex = 2of the array list - Gets the
itemfromindex = 2and asserts that it is equal to the uniqueitemadded in Step 5
Use the test results to help you debug your add(index, item) method, if needed.
Testing add(T item)
Similar to the test we just used to verify the add(index, item) and get methods, write another test called testAdd in your MyArrayListTest.java file that tests the add(item) method. You could even duplicate the code from our first test, only replacing which add function is called. Use the test results to help you debug your add(item) method, if needed.
Testing size()
Next, we can test our size method from the MyArrayList class using the testSize test automatically created by the IDE for us., since a stub for that test has already been created for us. Your test could:
- Create a new
MyArrayListobject, then assert that itssizemethod returns 0 (since the array list is empty) - Add an item to the object, then assert that its
sizemethod returns 1 - Add two more items to the object, then assert that its
sizemethod returns 3
Use the test results to help you debug your size method, if needed. Hint: if the size isnāt increasing properly, the problem might actually be in your add(index, item) method, which is responsible for increasing the value of the this.size instance variable.
Testing āEdge Casesā
Last but certainly not least, we also want to make sure that our implementation can handle what are called edge cases, which are special cases that are different from the general use case of a method and could cause unexpected behavior.
Additional edge cases for our MyArrayList class so far involve passing in an invalid index parameter into the add(index, item) and get methods. Write two more tests testAddException and testGetException that both:
- Create a
MyArrayListobject and fill it with a small number (e.g., 2-5) of items - Try to use way too high of an
MyArrayListparameter (e.g., 1000) to theaddorgetmethod and make sure that anIndexOutOfBoundsExceptionis thrown. You can check whether an exception is thrown by using theassertThrowsfunction used in Part 4 of the Warmup. - Repeat Step 2 but use the smallest
indexparameter that should throw anIndexOutOfBoundsException. (Question: what value should we use here based on the current size of the array list? It will be different foraddandget). - Repeat step 3, but use a negative
indexparameter and make sure that anIndexOutOfBoundsExceptionis thrown.
README
Most programming languages, including Java, only allow for positive indexing starting at the beginning of the list. So negative indices are never allowed in Java. Some programming languages, such as Python, do allow for negative indexing (where a negative index starts at the end of the list).
Committing Your Progress
Donāt forget to frequently save your progress periodically to GitHub using the add, commit, and push commands with git.