List

CSCI 1913 – Introduction to Algorithms, Data Structures, and Program Development
Adriana Picoral

List

An ordered, finite, mutable data structure

  • Plan out, in the abstract, the “object nature” and what behaviors you need to support
  • Write some example/test code

List

  • What is a list?
  • How do we use lists?
  • What methods do we need?

List

  • An ordered, finite, mutable data structure
  • We manipulate it (create list, add, append, remove, insert, get length, set an individual value)
  • append, prepend, insert, length, set, search

What about sort?

List methods

How do we do lists?

create list of a specific size, append, prepend, remove, insert, getLength, search, sort

List interface

Let’s implement a list interface.

  • Remember that an interface only contains method signatures.

Let’s start with the basic methods:

  • getter get(int index)
  • length() returns how many items in the list
  • set(int index, T value) – changes the value of item at index (return a bollean if value was set successfully)

List interface

Here’s our initial generic interface:

public interface List<T> {
    void append(T value);
    int length();
    T get(int index);
    void set(int index, T value);
}

List Implementation

  • Data structures, or abstract data types (ADTs) can be implemented using different approaches
  • We have our List interface, we will start by implementing our List with an array
  • We will call our class ArrayList and we will use the implements keyword:
public class ArrayList<T> implements List<T> {}

ArrayList

Here’s how we are setting up the private instance variable: private T[] data;

Constructor:

public ArrayList() {
  data = (T[]) new Object[size]; // define an arbitraty size
}

ArrayList

We will create a generic ArrayList that implements List

public class ArrayList<T> implements List<T> {
    private T[] data;
    private int size = 10;
    private int length  = 0;


    @SuppressWarnings("unchecked")
    public ArrayList() {
        data = (T[]) new Object[size];
    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public void append(T value) {
      // TODO: implement
    }

    @Override
    public T get(int index) {
        // TODO: implement
    }

    @Override
    public void set(int index, T value) {
        // TODO: implement
    }


}

Finish implementation

Submit your List.java and ArrayList.java files to gradescope.

public class UseList {
    public static void main(String[] args) {
        List<String> fruitList = new ArrayList<>();
        System.out.println(fruitList.length()); // 0
        System.out.println(fruitList); 

        fruitList.append("apple");
        fruitList.append("pear");
        fruitList.append("banana");

        System.out.println(fruitList);
    }
}

ArrayList append

@Override
public void append(T value) {
  data[length] = value;
  length++;
}

What happens when length is the same as size?

Expand the size of our array

@SuppressWarnings("unchecked")
private void expandArray() {
  size *= 2;
  T[] newData = (T[]) new Object[size];
  for (int i = 0; i < length; i++) newData[i] = data[i];
  data = newData;
}

append implementation

@Override
public void append(T value) {
  if (length == size) expandArray();
  data[length] = value;
  length++;
}

Submit to gradescope with array Expand

Submit your List.java and ArrayList.java files to gradescope making sure your array implementation can do multiple append calls (your implementation deals with array expansion)

Runtime of array Expand

The arrayExpand process is \(O(n)\) (gotta copy the array!) BUT it doesn’t run every time, so how do we handle the affect on append?

Best case: \(O(1)\) Worst case: \(O(n)\)

Runtime of array Expand

What we often care about is not the runtime of myList.append(4); but instead:

for (int i = 0; i < N; i++) {
  myList.append(i);
}

Runtime of array Expand

double array vs. increase array size by 10 plots

Why does it work this way

One way to think about it:

  • After an expand that costs N operations:
  • We get N “free” appends
  • Then we do 2N operations for next expand. (and so forth)

Runtime of array Expand

  • It is not technically true that append is \(O(1)\)
  • It IS true that \(O(n)\) appends costs \(O(n)\) time
  • We can divide that through and call append \(O(\frac{n}{n}) = O(1)\)
  • We can say “amortized runtime” \(O(1)\) instead of “runtime \(O(n)\)

Updated List interface

Let’s add prepend and insert to our interface.

void insert(int index, T value);
void prepend(T value);

When implementing ArrayList insert and prepend, we need to shift values in our array to the right

shiftRight

private void shiftRight(int startIndex) {
  length++;
  if (length == size) expandArray();
  for (int i = length-1; i > startIndex; i--) data[i] = data[i-1];
}

insert

public void insert(int index, T value) {
  if (index < 0 || index > length) throw new 
  shiftRight(index);
  data[index] = value;
}

prepend

public void prepend(T value) {
  shiftRight(0); // need to shift all the other values
  data[0] = value;
}

Submit to gradescope

Submit your List.java and ArrayList.java files to gradescope making sure your array implementation contains methods prepend and insert.

Changing size of the list – removing from list

  • Add T pop(int index) to our List interface

We need to implement shiftLeft in ArrrayList

Test your ArrayList implementation

public class UseList {
    public static void main(String[] args) {
        List<String> fruitList = new ArrayList<>();
        System.out.println(fruitList.length()); // 0
        System.out.println(fruitList);

        fruitList.append("apple");
        fruitList.append("pear");
        fruitList.append("banana");
        fruitList.prepend("kiwi");
        fruitList.prepend("tomato");
        fruitList.insert(2, "pineapple");

        System.out.println(fruitList);
        System.out.println(fruitList.pop(1));
        System.out.println(fruitList);
    }
}

Submit to gradescope

Submit your List.java and ArrayList.java files to gradescope making sure your array implementation contains the pop method.

ArrayList implementation

public class ArrayList<T> implements List<T> {
    private T[] data;
    private int size = 10;
    private int length  = 0;

    @SuppressWarnings("unchecked")
    public ArrayList() {
        data = (T[]) new Object[size];
    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public void append(T value) {
        if (length == size) {
            expandArray();
        }
        data[length] = value;
        length++;
    }

    @Override
    public void insert(int index, T value) {
        if (index < 0 || index > length) throw new RuntimeException("index our of range");
        shiftRight(index);
        data[index] = value;
    }

    @Override
    public void prepend(T value) {
        shiftRight(0); // need to shift all the other values
        data[0] = value;
    }

    @Override
    public T pop(int index) {
        if (index < 0 || index >= length) throw new RuntimeException("index out of range");
        T out = data[index];
        shiftLeft(index);
        return out;
    }

    private void shiftRight(int startIndex) {
        length++;
        if (length == size) expandArray();
        for (int i = length-1; i > startIndex; i--) data[i] = data[i-1];
    }

    private void shiftLeft(int startIndex) {
        for (int i = startIndex; i < length; i++) data[i] = data[i+1];
        length--;
    }

    @SuppressWarnings("unchecked")
    private void expandArray() {
        size *= 2;
        T[] newData = (T[]) new Object[size];
        for (int i = 0; i < length; i++) newData[i] = data[i];
        data = newData;
    }

    @Override
    public T get(int index) {
        if (index < 0 || index >= length) throw new RuntimeException("index out of range");
        return data[index];
    }

    @Override
    public void set(int index, T value) {
        if (index < 0 || index >= length) throw new RuntimeException("index out of range");
        data[length] = value;
    }

    @Override
    public String toString() {
        String out = "";
        for (int i=0; i < length; i++) out += data[i] + " ";
        return out.trim();
    }


}

Array-based List Runtime Summary

Front of List Middle of List End of List
get O(1) O(1) O(1)
set O(1) O(1) O(1)
insert O(n) O(n) O(1)
append O(n) O(n) O(1)
prepend O(n) O(n) O(n)
pop O(n) O(n) O(1)

Quiz 07

You have 10 minutes to complete the quiz