Algorithms and OOD (CSC 207 2014F) : Labs

Laboratory: Implementing Lists with Arrays

Summary: We extend our understanding of a list ADT by considering an implementation of lists in which the values are stored in an array. We explore two kinds of iterators in Java, basic iterators and list iterators.

Prerequisite Knowledge: Arrays. Interfaces. Generics. Anonymous inner classes.

Preparation

a. Fork and clone the repository at https://github.com/Grinnell-CSC207/lab-array-based-lists.

b. Import that repository into Eclipse. (In case you've forgotten, you should use File > Import > Exiting Projects into Workspace and then select the local directory for the repository.)

c. In a separate window or tab, open the documentation for `Iterator` and `ListIterator`.

Exercises

Exercise 1: Understanding Iterators

Skim through the documentation for `Iterator` and `ListIterator`. You should identify the primary methods, their meanings, and any subtleties.

Make sure that you can answer the following questions. You may find it useful to run some experiments to check your answers. The file `ArrayListExperiment.java` is a good starting place for your experiments.

a. Where, conceptually, is an iterator relative to the elements of a list?

b. What element does `remove` remove? For example, if an iterator, `it`, is between the a and the b in the list [a,b,c] and we call `it.remove()`, which value does it remove?

c. Suppose we have a list iterator, `lit`, and call `lit.add(x)` and then immediately after call `lit.add(y)`. In what order will `x` and `y` appear in the list?

d. Suppose we have a list iterator, `lit`, between the a and b in the list [a,b,c]. Suppose we then call `lit.remove()` and then immediately after call `lit.remove()` again. What can or should happen?

e. Can we add an element using a list iterator immediately after creating that list iterator? Why or why not?

f. Can we remove an element using a list iterator immediately after creating that list iterator? Why or why not?

g. Can we set an element using a list iterator immediately after creating that list iterator? Why or why not?

h. Suppose we've created two list iterators, `lit1` and `lit2` for the list [a,b,c], and both are between the a and the b in the list. If we call `lit1.add(d)`, what should `lit2.next()` return?

i. Suppose we've created two list iterators, `lit1` and `lit2` for the list [a,b,c], and both are between the a and the b in the list. If we call `lit1.remove()`, what should `lit2.next()` return?

j. What other subtleties, if any, have you noticed about these two kinds of iterators?

Exercise 2: A Simple List Interface

Look at the `SimpleList` interface. You will note that it contains only two methods, `iterator` and `listIterator`.

a. You may recall that we decided that lists are dynamic, user-ordered, homogeneous collections of values. Does the `SimpleList` interface achieve that goal? Why or why not?

a. You may also recall that we decided that lists need methods to add elements (at the front, at the back, at particular positions). How do we add elements to `SimpleList`s? Can we control where in the list those elements go?

b. You me recall that we decided that lists need methods to remove elements. How can we remove elements from `SimpleList`s?

Exercise 3: A Simple Experiment

Read through the code of `SimpleListExpt.java` and `SALExpt.java`.

a. Sketch the output you expect to see from `SALExpt`.

Exercise 4: Implementation Details

The class `SimpleArrayList.java` contains an array-based implementation of the `SimpleList` interface. How are these array-based-lists implemented? It's time to look. We'll start with fields. Once we've examined the fields, we can start considering the implementation of various methods.

Here are the two basic fields. (There are a few others.)

```public class SimpleArrayList<T>
implements SimpleList<T>
{
/**
* The array used to store the list elements.
*/
T[] values;

/**
* The number of values actually in the list.
*/
int size;
```

Of course, most of the work happens in the iterators. Because these iterators will need to access the fields of the `SimpleArrayList` that they are iterating, we use anonymous inner classes. The anonymous inner class has one main field, `pos`.

```  public ListIterator<T> listIterator()
{
return new ListIterator<T>()
{
/**
* The position in the list of the next value to be returned.
*/
int pos = 0;

...
}; // new ListIterator<T>
} // listIterator()
```

a. Sketch how you would implement the `next` method.

b. Compare your answer to that in the code. You can ignore the call to `failFast`. We'll come back to that in another exercise.

c. Sketch how you would implement the `hasNext()` method.

e. Sketch how you would implement the `add(T val)` method.

Exercise 5: Implement `set`

You may note that the `set` method is not yet implemented.

a. Write a small experiment that will let you check if `set` works. For example, you might create a list of five values and set elements 0, 2, and 4 to other values. Don't bother checking the extreme edge cases, such as what happens when there has not been a prior call to `next` or `prev`.

b. Here's a simple strategy for implementing `set`. Since `pos` represents the location of the value to be returned by `next`, `pos-1` represents the location of the value that was last returned. Hence, all `set` needs to do is set the value in the array at that location.

What flaws, if any, do you see in this strategy?

c. Implement `set` using this strategy and, through your experiment, determine whether or not it seems to work.

d. If we use this strategy, one time that `set` will fail is when `pos` is 0. Update your program so that it throws an exception in such cases.

e. As you may have noted, a possible flaw in this implementation is that, while it works when we move forward with `next`, it will likely fail when we use `prev`. Sketch a strategy for dealing with this problem. (You do not need to implement the strategy.)

Exercise 6: Failing Fast

You may recall that in exercise 1, we asked what happens when we mutate a list using one iterator and then try to access it using another iterator for the same list. You probably found that the documentation for iterators is vague on this issue. You should have also noted that the vagueness is problematic. So, what should we do?

Let's see what the designers of Java did by looking at the standard `java.util.ArrayList` class.

The iterators returned by this class's `iterator` and `listIterator` methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own `remove` or `add` methods, the iterator will throw a `ConcurrentModificationException`. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

a. If you look at `SimpleListExpt.java`, you'll see a method called `failFastExpt`. Explain to your partner how this allows us to understand fast failure.

b. Add the following line to the `main` method of `SALExpt.java` so that we can see if our simple array-based lists fail fast, at least in a simple situation. (After adding the line, you should recompile and run `SALExpt`.)

```    SimpleListExpt.failFastExpt(pen, new SimpleArrayList<String>());
```

c. Suppose you were called upon to implement the “fail-fast” policy. How would you achieve that goal?

d. Read through the code for `SimpleArrayList.java` to see how it achieves the “fail-fast” policy.

For Those With Extra Time

Extra 1: Moving Backwards

You'll note that the `previous` method is not implemented. Implement it.

Once we implement `previous`, we are likely to break the `set` method we defined earlier. Fix that method.