Comp201: Principles of Object-Oriented Programming I
Spring 2008 -- Lab 15: Writing Loops and Processing Arrays   


To kill two birds with one stone, we will first go over the sorting portion of last year's Exam 3 as it involves both array processing with loops and template pattern-based sorting.


In this lab, we will practice writing loops to process arrays.

The big problem with arrays is that they just don't quite have enough intelligence. We need a smarter array, one that can automatically resize itself to add more elements, will shrink when elements are removed, and supports various odd and sundry other features that might be handy. In everyday Java parlance, we want to create a "vector" (see the Java documentation for java.util.Vector for comparison).

Creating a Smarter Array

Download the supplied lab code: lab15.zip

Basically, if we want to give an array more intelligence we have to "wrap" it in another class. We cannot subclass the array because all arrays are final and thus not able to be subclassed. But actually, composing with an array gives us the ability to achieve the dynamic reclassification-type behavior needed to make the array grow and shrink.

SmartArray holds an array that is at least as large as is presented to the outside world (an apparent array). Internally, it keeps a maximum allowable index value, _next, which tells it where the end of the apparent working array is located within the larger storage array, _data. So long as _next <=_data.length then the apparant array can grow and shrink at will.

If _next exceeds _data.length, then we simply need to replace _data with a new array that is twice as large and copy all the data over to the new storage array. Whenever we need to increase the size of the array, it is important that we increase by a constant multiplicative factor that is greater than one, as opposed to increasing it by a fixed additive constant. In doing so, the cost for increasing the size is amortized over time, and we can prove that the cost for adding an element to the array is bounded by a fixed constant. That's a lot of fancy words that means: if you double the size of the array every time you need to increase its size, after you add lots and lots of new elements, you find that total expense of copying the array over when necessary becomes a fixed cost on a per element basis.

Study the code for SmartArray to see how the following methods work:

Notice how the index bounds have to be checked over and over again. No polymorphism to help us out here!

Write the code for the methods with missing bodies, in this recommended order:

  1. SmartArray toSmartArray(Object[] source)-- need to write the loop to copy the input array. Returns a new SmartArray.
  2. String toString(Object[] elts) -- Watch these situations: length = 0 and the last element in the array.
  3. String toString() -- this shows you the entire storage array plus the value of _next.
  4. void add(Object elt)-- if the storage array (_data) is too small, create a new one that is twice as large and copy all the old and the one new element over to it. Set _next appropriately. See the test case for example details.
  5. boolean contains(Object x) -- returns true if x is somewhere, at least once, in the apparent  array.   Try writing this method using the standard "counted" for loop as well as the "for each" loop style. 
  6. Object remove(int ix)-- Check that ix is valid. Write a loop to shift the elements over to fill the empty spot. Which elements need to be shifted. Set _next appropriately. Study the test case carefully to make sure you understand the actual behavior of remove!
  7. Object[] toArray()-- copy only the relevant storage array elements into a new array.
  8. SmartArray remDup()-- For each element, remove any duplicates of it in the array.
  9. SmartArray unzip() -- If you remove an element, what is the next element to process?
  10. void zip(SmartArray other)-- tricky in spots--be careful and consider all the possibilities!

Don't you miss polymorphism now? Do you see why it's better to let the objects take care of themselves than to have to check everything all the time?

 

Additional Exercises:

  1. Create a class called util.ArrayUtil with the following method:
    1. public static boolean equals(Object[] x, Object[] y) -- takes 2 arrays of Objects and returns a boolean true if they are equal, which means that
      • x and y are the same length.
      • for all valid indices, i, x[i].equals(y[i[) returns true
      • if x[i] is null, then y[i] is null as well.
      • These specifications match those of the Sun-supplied java.util.Arrays.equals(Object[] x, Object[] y) method.
      • To test your code, substitute ArrayUtil.equals() in the body of the arrayEquals() method in the test class.
  2. The remDup method your wrote above traverses the array way too many times if there are multiple duplicates in the array. This is because remove will traverse the entire array for each element removed. Write another version called remDupBetter that doesn't use remove but instead incorporates the shifting of elements itself. This new implementation should traverse the array only once per duplicate found.
  3. Likewise, if you used remove in your unzip method, that can algorithm probably be improved by writing your own custom loops for this situation. Try writing an unzipBetter which only traverses the list once. This is a tough algorithm. If you can do this one, you will be a pro at loops and arrays!
  4. Try writing the for loops above as while loops.

 


Last Revised Thursday, 03-Jun-2010 09:50:28 CDT

©2008 Stephen Wong and Dung Nguyen