1 Creating Lists in Java: the Linked List class
1.1 Integer versus int
1.2 Importing Linked List
2 Operations on Lists: Adding and Counting Elements
3 Iterating Over Java Lists
4 Using Linked Lists to Provide ISets

Java’s Built-In Lists

Kathi Fisler

Lists are built into Java. There are several variations of them. We will start with the LinkedList class in this course. Since you know lists conceptually, we’ll do this intfoduction largely through examples.

1 Creating Lists in Java: the LinkedList class

First, let’s create an empty linked list of strings, called Words:

  LinkedList<String> Words = new LinkedList<String>();

The <String> tells Java what type of data will go into the list. Since lists could contain any kind of data, Java needs to know what type of data is in any particular list to help it check types at compile time. If instead we wanted a list of numbers (say containing the points earned on each question in a quiz), we would write

  LinkedList<Integer> QuizPoints = new LinkedList<Integer>();

1.1 Integer versus int

Notice that we used <Integer> instead of <int> (as this course has used all along for numbers). The type given to a LinkedList must be the name of either a class or an interface. In Java, Integer is a class that holds a number, whereas int is just a number by itself (without a class). Operations on ints are faster than the corresponding ones on Integer (since there is no class involved), but if you need to treat a number as a class, you must use Integer instead.

1.2 Importing LinkedList

We have to tell Java whenever our code uses its built-in classes (beyond simple kinds of data like String). You’ve already experience the idea of import when you configured the Tester. Similarly, to use LinkedList class, we have to import the class definition into our program. So a complete file with our two list definitions would look like:

  import java.util.LinkedList;

  

  class Examples {

    LinkedList<String> Words = new LinkedList<String>();

    LinkedList<Integer> QuizPoints = new LinkedList<Integer>();

  }

Naturally, you can use LinkedList inside more than just the Examples class (we will do so shortly); we’re putting the lists in the Examples class here to give a simple way to play with the lists for now.

2 Operations on Lists: Adding and Counting Elements

The methods for adding elements to the front of a list and getting the length of a list are called addFirst and size, respectively. Here’s an example of of them, written as a series of interactions in DrJava.

  > import java.util.LinkedList

  > LinkedList<String> Words = new LinkedList<String>();

  > Words

  []

  > Words.size()

  0

  > Words.addFirst("pie")

  > Words.size()

  1

  > Words.addFirst("corn")

  > Words.size()

  2

  > Words

  [corn, pie]

If you instead wanted the final list to have pie first and corn second (ie, adding puts new elements on the end of the list), you would use the add method instead of addFirst. We’re using addFirst today as it matches the behavior of cons from Racket.

Something to note here is that the list methods in Java modifies the original list (which was not true in Racket–cons left the original list intact, and made it the programmer’s responsibility to save the new list). Once you add an element to a list in Java, you no longer have access to the list without the element.

What does this sequence suggest is the return type of the addFirst method? Nothing prints, so it seems to return void. Try the following interaction:

  Words.addFirst("milk").addFirst("tofu")

Java raises an error that "No method in void has name ’addFirst’". In Java, addFirst does indeed return void, so you cannot chain together expressions as you might have been used to in Racket.

Both of these traits of addFirstmodifying the original list and returning void—have interesting implications for how we program. We will discuss these issues as they come up throughout the course.

If you want to see the full list of operators provided on LinkedLists, check out the official LinkedList documentation.

3 Iterating Over Java Lists

Let’s write a method to sum up the numbers in a LinkedList of integers. Java does not have an operator corresponding to rest in Racket. This changes how we write programs over lists: in particular, we cannot simply call a function recursively on the rest of a list. Instead, Java provides a for construct that iterates over lists. Here it is used to compute sum:

  int sum (LinkedList<Integer> numList) {

    int result;

  

    result = 0;

    for ( Integer n : numList ) {

      result = result + n;

    }

    return result;

  }

To understand the structure of this code, it helps to step back and see the pieces of the construct. In the following code skeleton, substitute single expressions for terms in all-caps, and substitute computations for descriptions in square-brackets:

  RETURN-TYPE result;  // a local variable (field) in the class

  

  result = [the result when the list is empty]

  for ( TYPE VARNAME : SPECIFIC-LIST ) {

    result = [expression to build new result from current

              value of result and VARNAME];

  }

  return result;

For those who took 1101, this is just the accumulator-style of programs that you did in the last segment of that course.

4 Using LinkedLists to Provide ISets

Previously, we saw how to implement our own BST classes and use them to implement ISet. How do we implement ISet using Java’s built-in classes? We cannot edit the extends clause on a built-in class to make it also implement ISet, so clearly we have some extra work to do.

In this case, we will simply create a new class that implements ISet, but internally uses LinkedList and its operators to provide the required interface. Here’s the code:

  class ListSet implements ISet {

    LinkedList<Integer> theSet;

  

    ListSet() {

      this.theSet = new LinkedList<Integer>();

    }

  

    // adds element to the set if it is not already there

    public ISet addElt(int elt) {

      if (!theSet.contains(elt)) {

        theSet.addFirst(elt);

      }

      return this;

    }

  

    // removes the element from the set.  The remove

    //   operator on lists removes only one occurrence,

    //   so this relies on addElt not adding any element

    //   more than once

    public ISet remElt(int elt) {

      theSet.remove(elt);

      return this;

    }

  

    // uses the built-in list operator to compute the size

    public int size() {

      return theSet.size();

    }

  

    // uses the built-in list operator to check for elements

    public boolean hasElt(int elt) {

      return theSet.contains(elt);

    }

  }

Note that the fact that we are working with lists is all hidden inside the ListSet class. Most of the operations are straightforward, though we do have to decide how to maintain the Set ADT property of no duplicates; here, we took the easy route of only adding elements that are not already in the list.

Finally, if you look at our LotteryTicket example again, you see that we can change that system to using lists instead of BSTs without editing inside the LotteryTicket class: we just edit the place where we create the LotteryTicket to start from an empty ListSet rather than an MtBST

  class LotteryTicket {

    ISet numbers;

  

    LotteryTicket(ISet initSet) {

      this.numbers = initSet.addElt(5);

      this.numbers = this.numbers.addElt(23);

      this.numbers = this.numbers.addElt(6);

    }

  }

  

  class Examples {

    Examples(){}

  

    LotteryTicket LT1 = new LotteryTicket(new ListSet());

  }