Close
Close Window

«  7.2. The List Interface   ::   Contents   ::   9.1. References in Java  »

8.1. ArrayList Implementation

8.1.1. Full Implementation

Below is a complete implementation for the array-based list, named MHCArrayList. MHCArrayList must implement all of the methods of the MHCList interface.

Note

We provide the full implementation for completeness, but we will focusing on only a few methods in the following sections.

import java.util.Arrays;

/**
 * Array-based list implementation of the MHCList interface.
 */
public class MHCArrayList<E> implements MHCList<E> {
    private E[] elements;   // Array holding list elements
    private int capacity;     // Maximum number of values that fit in the array
    private int size;    // Current number of elements in the list

    // Constructor: creates a list with a default capacity of 10
    @SuppressWarnings("unchecked") // Generic array allocation
    public MHCArrayList() {
        elements = (E[]) new Object[10];  // Initialize elements array to 10 by default
        capacity = elements.length;
        size = 0;
    }

    // Replaces the element at the specified position in this list with the specified element.
    public void set(int position, E o) {
        // Check that the position is valid
        if (position < 0 || position >= size) {
            throw new IndexOutOfBoundsException(position);
        }
        elements[position] = o;
    }

    // Returns the element at the specified position in this list.
    public E get(int position) {
        if (position < 0 || position >= size) {
            throw new IndexOutOfBoundsException(position);
        }
        return elements[position];
    }

    // Returns the number of elements in this list.
    public int size() {
        return size;
    }

    // Helper method to resize the array when it's full
    private void grow() {
        capacity = 2 * capacity;

        // Utility method that copies elements into a new array with the larger capacity
        elements = Arrays.copyOf(elements, capacity);
    }

    // Appends the specified element to the end of this list.
    public boolean add(E o) {
        // If the array is full, make it bigger.
        if (size >= capacity) {
            grow();
        }
        elements[size] = o;
        size++; // We've added an element, increase the size of the list
        
        // indicate that the add was successful
        return true;
    }

    // Inserts the specified element at the specified position in this list.
    public void add(int position, E o) {
        if (position < 0 || position > size) {
            throw new IndexOutOfBoundsException(position);
        }
        // If the array is full, make it bigger.
        if (size >= capacity) {
            grow();
        }
        // Shift elements to the right to make space at the position
        for (int i = size; i > position; i--) {
            elements[i] = elements[i - 1];
        }

        elements[position] = o;
        size++; // We've added an element, increase the size of the list
    }

    // Removes the element at the specified position in this list, and returns it.
    public E remove(int position) {
        // Check that the position is valid
        if (position < 0 || position >= size) {
            throw new IndexOutOfBoundsException(position);
        }
        
        E removedElement = elements[position];

        // Shift elements to the left
        for (int i = position; i < size - 1; i++) {
            elements[i] = elements[i + 1];
        }
        size--; // Update size
        
        return removedElement;
    }

    public boolean isEmpty() {
        return this.size == 0;
    }

    // Returns a string representation of the list elements
    public String toString() {
        // use square brackets to indicate beginning/end of list
        String out = "[";
        for(int i = 0; i < this.size; i++) {
            out = out + this.elements[i].toString();

            if (i < this.size - 1) {
                out = out + ", "; // add a comma separator if this is not the last element
            }
        }
        out = out + "]";

        return out;
    }
}

8.1.2. How ArrayLists internally represent data

Note

Make sure to click through all the animations until the green check mark shows up to see all the content!

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

8.1.3. Instance variables and constructors

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

Below is the constructor for our MHCArrayList class:

    // Constructor: creates a list with a default capacity of 10
    @SuppressWarnings("unchecked") // Generic array allocation
    public MHCArrayList() {
        elements = (E[]) new Object[10];  // Initialize elements array to 10 by default
        capacity = elements.length;
        size = 0;
    }

We create a new empty array named elements that can hold 10 elements and initialize the capacity of the ArrayList to the length of the elements array. Because the programmer is creating a new MHCArrayList object, there are no data elements stored within it and so the size of the list begins at 0.

You’ll notice that there is something a little unusual going on in the initialization of the elements array:

elements = (E[]) new Object[10];  // Initialize elements array to 10 by default

Since the type E is generic, we actually cannot instantiate an array of type E. We instead have to create an array of Object and cast or “convert” it to a type of E[], which is done with the parentheses: (E[]).

Note

We generally want to avoid using casting and instead use polymorphism to work with classes and subclasses. This is one of the few times we’ll see casting this semester!

You’ll also notice a line that begins with @ above the constructor, which is called an annotation:

@SuppressWarnings("unchecked") // Generic array allocation

We include this because the Java compiler knows that casting to an unknown type E[] is a risky operation, so it will display a warning message if we compile the code. The annotation tells Java not to worry, and that we really do want to make this cast. But this again goes to show that casting should be used sparingly, if at all!

Note

Don’t worry about the specific details of this constructor – we have to use this syntax because of the way Java handles generic types. This is the only time we’ll see this syntax this semester.

8.1.4. Adding elements to a given position: add(int index, E o)

Because the array-based list implementation is defined to store list elements in contiguous cells of the array, the add and remove methods must maintain this property.

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

8.1.4.1. add() Interactive Exericse

8.1.5. Adding elements to end of list: add(E o)

Note

Notice that we can define two methods named add() with different behaviors. That’s because the two methods have different parameters, so Java can differentiate between which method is being called by the parameter types being passed in. This is known as overloading.

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

8.1.6. Removing elements at a given position: remove(int position)

Removing an element from the beginning of the list is similar to add() in that all remaining elements must shift toward the beginning by one position to fill in the gap. If we want to remove the element at position \(i\), then \(n - i - 1\) elements must shift toward the head, as shown in the following widget.

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

In the worst case, insertion or removal each requires moving either \(n\) or \(n-1\) elements, which is \(O(n)\).

8.1.6.1. remove() Interactive Exericse

   «  7.2. The List Interface   ::   Contents   ::   9.1. References in Java  »

Close Window