10:00pm, Sunday, 24 September
In this lab, you will create your first implementation of a data structure from the Java Collections Framework. You will also learn how to use Generics and get some practice writing test cases for you program. The purpose of this lab is to:
You'll be constructing your very own implementation of the ArrayList data structure so that you understand its inner workings. You should use the prefix "My" in your class names so that you don't accidentally use the standard ArrayList in your testing. However, you will be matching its behaviour, so when in doubt, you can refer back to the ArrayList documentation for clarification and/or use an ArrayList as a working reference solution.
Your MyArrayList class will implement the Java
List
interface. That is, you will need to provide implementation for all of the
(abstract) methods contained therein. There are more than 20 such methods,
however, and that could take you awhile to get through. Moreover, some of
the methods are "redundant" in the sense that they can be implemented using
the other methods (for example, you can implement isEmpty()
by returning
(size()==0)
). Fortunately, the folks at Java have provided a lovely
abstract class
AbstractList
that provides some very basic and default behavior for a List. Some of the
methods still aren't implemented (that is, they are abstract) and some of
them may have inefficient implementation (that is, you'll want to override
them), but it's useful nonetheless. Thus your MyArrayList class should
extend AbstractList in order to reap the benefits; because AbstractList
implements the List interface, you will implicitly be required to do so as
well (but do not have to declare your intention to implement explicitly).
You'll be using generics for your implementation, and taking advantage of the fact that List and AbstractList are similarly parameterized. Just declare your class like the following:
public class MyArrayList<AnyType> extends AbstractList<AnyType>
AbstractList gives you all methods except for get(int index) and size(), however, you'll need to override many other methods to get your ArrayList in fighting form.
As you might expect from the name, the backing storage for an ArrayList is an array. That is, an ArrayList is really just an array with the extra functionality of dynamic resizing. The MyArrayList's array will not always be full because an ArrayList may change its size when the underlying array does not; therefore, your class will need to keep track of the number of items that are currently in the underlying array according to the ArrayList (which will at most times be different than the capacity/length of the array). Also, you need to keep the data in the array packed to the front so there are no gaps.
Add the following members to your MyArrayList class.
Implement the following constructors in your MyArrayList class.
You may get a compiler warning about allocating an array of a generic type. This can be solved by using casting and a special marker in the source code to suppress the warning. Allocate in a manner similar to:
AnyType[] someArray=(AnyType [])new Object[numElements];
and have a line
@SuppressWarnings("unchecked")
between your JavaDoc comments and the method header. This doesn't remove the warning in 1.5, but does in 1.6, and it will get rid of the error message in Eclipse.
You will need to implement a private resize method that doubles the size of the array (do not just increase its length by one. Hopefully you saw on the prelab how inefficent that is!) by creating a new array of twice the current size, copying the data over, then resetting the data pointer to point to the new array.
data.length
)
this.data
to be your new array
Feel free to write more private methods as you see fit.
Implement the following methods in your MyArrayList class
data.length
)
index==size
is the first unused location) Resize the
array if necessary.
throw new IndexOutOfBoundsException();or even better:
throw new IndexOutOfBoundsException("Index Out of Bounds! You tried to get " + index + " but the size is " + size );Or something to that effect.
Don't forget to be adding in your JavaDoc style comments as you go along.
When the array is full, and a user tries to add something in, you should double the size of the array (rather than just increase the size by one), copy the old array into the new array, and add the new element. You'll need to copy the previous items into the same slots in the new array. You should probably have a private method for this array resizing. I'd suggest printing yourself a message when it is called at first indicating what size it is and what size it becomes. Just be sure to remove it after doing some testing.
Also, take advantage of your existing methods and don't duplicate logic. You've got two constructors and two add() methods. Only one needs to do the real work, the second should just figure out a way to call the first.
You should include a meaningful message when you construct the IndexOutOfBoundsException. (For example, when Java's ArrayList throws an IndexOutOfBounds exception, it tells you which index was accessed and the size of the arraylist. This seems like good information.)
We now interrupt this lab to bring you an important message about testing.
The programs you will write in this course get increasingly complex, and increasingly difficult to debug. We want to strongly encourage you to get into good testing habits now, right off the bat. Many of you have survived thus far by writing allll your code for a lab in one feel swoop and then testing (if that) as an afterthought at the end. Some of you are even strangely proud of this fact. This is no longer a feasible approach. Your life will be much simpler if you "write a little, test a little". So before you proceed to the rest of your MyArrayList implementation, you will test the code you have written thus far, using a fancy Java testing framework called JUnit.
As a side note, some people take this to the extreme, and practice "Test Driven Development." As the name suggests, instead of designing your program to fit some guidelines and THEN deciding what tests are adequate, test driven development FIRST decides on a set of tests that you want your program to "pass", and only then do you design the program to pass those tests. You can read more about this software development technique in many places, including Wikipedia.
The "old" way of testing was to write test cases in a main method somewhere. Although this approach is convenient and simple, it can be ineffective:
The JUnit framework addresses these issues, and more.
JUnit is a widely-used API that enables developers to easily create Java test cases. It provides a comprehensive assertion facility to verify expected versus actual results.
It is simple to write a JUnit test case, especially in Eclipse:
MyArrayList<Integer> test = new MyArrayList<Integer>(); ArrayList<Integer> real = new ArrayList<Integer>(); assertEquals("Size after construction", real.size(), test.size());Basically, we construct an arraylist with our implementation, and compare it to that of Java's. Ideally the size of our new arraylist is 0, which should be real.size() anyway. Observe that this is a stand-alone test; it constructs the array lists it needs, and is disjoint from the other tests.
assertEquals("Size after construction", 2, test.size());Rerun the test (press the green play-button icon with the yellow arrow), and see how the test fails. Notice that the failure trace conveniently tells you what value you were expecting (2), and what value you actually got (0). How useful is that! Change it back.
MyArrayList<Integer> test = new MyArrayList<Integer>(); ArrayList<Integer> real = new ArrayList<Integer>(); assertEquals( "Size after construction", real.size(), test.size()); test.add(0,5); real.add(0,5); assertEquals( "Size after add", real.size(), test.size());
@Test(expected=IndexOutOfBoundsException.class) public void testForAddLeftException() throws Exception { MyArrayList<Integer> test = new MyArrayList<Integer>(); test.add(-1, 5); }To test the exception that should be thrown by the add method when adding "off the right" of the list, we would use a second method:
@Test(expected=IndexOutOfBoundsException.class) public void testForAddRightException() throws Exception { MyArrayList<Integer> test = new MyArrayList<Integer>(); test.add(test.size()+1, 5); }
Now let's have you try. In each of the following test methods, implement the recommended test.
Read Strings from the file test1.txt, creating a MyArrayList of Strings (and probably also an ArrayList of the same strings) by inserting each line of the file, one at a time, into a MyArrayList<String> using the add(element) method. Then loop through your two lists element-by-element and assert that the ith element should be equal for each i.
Again, read Strings from test1.txt, but insert each line at the front of the list using add(index, element).
Add on to the end of the previous test by reconstructing your two arrays. Perform the same steps, but insert each line at the midpoint (size/2) of the list.
There you go, your very first JUnit tests! As you implement the rest of your arraylist methods, please write the accompanying JUnit test, and add to any previous ones.
Now spend some time finishing up your implementation of your MyArrayList class. Don't forget to write (and run!) your tests as you go along.
Implement the following methods in your MyArrayList class
null
.
Great! You are done your implementation, and you have written some JUnit tests to make sure that each method works. Hopefully everything is being added, set, and removed correctly. The last thing we want to quickly check is that your program is working efficiently. Add the following two tests to your test file (precede the method header with the @Test tag):
You may want to change this method's @Test tag to an @Ignore tag before proceeding. This will indicate to JUnit to ignore this test for the time being (which is good, because it takes awhile to run.)
% java -Xmx500m Test8
Also, pay attention to the rate at which it prints out numbers. Do you notice any changes? Run it again. Does it behave the same? Why do you think this is happening?
Please put your answers to the above questions in a plain text file named README file, and submit it with your lab.
***** After you have completed these tests, please put @Ignore in front of the @Test to disable them. *****
Look through your programs and make sure you've included your name at the top of all of them. If you know that there is a problem in one of your programs, document that at the top. If you adhered to the honor code in this assignment, add the following statement as a comment at the top of your README, MyArrayList.java and MyArrayListTest.java files:
I have adhered to the Honor Code in this assignment.
Quit Eclipse, and then go through and delete any *.class files and backup files. (If you don't quit Eclipse first, it will keep regenerating your class files when it autocompiles!)
You now just need to electronically handin all your files.
% cd # changes to your home directory % cd cs151 # goes to your cs151 folder % handin # starts the handin program # class is 151 # assignment is 2 # file/directory is lab2 % lshand # should show that you've handed in something
You can also specify the options to handin from the command line
% cd ~/cs151 # goes to your cs151 folder % handin -c 151 -a 2 lab2 % lshand # should show that you've handed in something