Chapter 7 - Java for Beginners Course

List and ArrayList - Basic Operations

List is a type of collection that offers the following basic features:

  • Elements are ordered in a sequence, one after the other, similar to what we saw with arrays.

  • All elements have a position/index associated with them. This means you can get/insert elements at specific positions in the list. It also means you can search for the indexOf a particular element.

  • Elements can be duplicated. This means, the same object in the list can appear more than once.

For example, we could have the following list of elements:

Example of list of Strings of 4 elements

As mentioned before, List is an interface that defines the contract of the operations provided by different types of list implementations. In this section, we’ll make use of the ArrayList class as the concrete implementation to show some examples.

An ArrayList, as the name suggests, stores the elements inside of an array. However, this class will automatically manage the cases where the array size limit is reached and will resize the array automatically for us.

Some of the methods that we’ll introduce in this section come directly from the Collection interface, which means they are available not only for List but also for other types of collections (for example, also available to Set).

Creating an ArrayList

Let’s create an ArrayList to hold the example list of strings in the image above:

In the example code run the JavaBasicArrayListApp
List<String> sentence = new ArrayList<>();
System.out.println("Initial size of the list: " + sentence.size());

Output:

Initial size of the list: 0

In this case we’re creating a variable called sentence that represents our list of strings. At this stage, the sentence list of strings is empty, it has just been created hence it has no elements.

We can check the number of elements inside any Collection using the size() method as with the example above.

Note that it is good practice to use the interface type instead of the implementation type for the variable, to reduce the dependency your code will have to behaviors that might be implementation-specific. For example, above we’re using List<String> sentence instead of ArrayList<String> sentence.

Adding elements

To insert elements into a List we can make use of the add methods. To recreate our list in the image above, we could do:

List<String> sentence = new ArrayList<>();

sentence.add("This");
sentence.add("is");
sentence.add("a");
sentence.add("sentence");

Using the add(E element) method causes the elements to be added automatically at the end of the list.

The add(E element) method can be used with any Collection. However, List offers an overloaded add(int index, E object) method where we can specify the position where the element should be inserted. For example, sentence.add(0, "Test") would add the "Test" string at the beginning of the list.

Retrieving an element by index

One of the features of a List is that we have a method get(int index) that allows us to retrieve the element in a specific position of our list. Similar to arrays, this index is zero-based and is the equivalent of doing array[index], for example, if we want to get the second word of our sentence:

String secondWord = sentence.get(1);
System.out.println("Second word: " + secondWord);

Output:

Second word: is

Iterating over elements

To iterate over elements, we can use similar approaches to what we did with arrays, we can use a standard for loop or an enhanced-for loop:

For example, to print out the words in our sentence list of strings, we can do either of these:

for (int i = 0; i < sentence.size(); i++) {
    String word = sentence.get(i);
    System.out.println(word);
}

or

for (String word : sentence) {
    System.out.println(word);
}

Output:

This
is
a
sentence
As discussed before, since Java 8 there are more ways of doing this. These will be covered in a later chapter after we introduce the concept of lambdas.

Checking if an element exists

Another operation that we can perform with List and Collection in general is to check if an object already exists inside our collection of elements. The contains(E element) method will return true if the element already exists in our collection or false otherwise.

For example:

System.out.println("Sentence contains a: " + sentence.contains("a"));
System.out.println("Sentence contains b: " + sentence.contains("b"));

Output:

Sentence contains a: true
Sentence contains b: false
The contains and indexOf operations rely on the equals method of the objects in the list.

Checking where an element is

In particular for List, not only we can check if an element exists, we can also check in what index it is placed. The indexOf(E element) method will return an integer that represents the position where the element is stored in the list, or a negative value (-1) if it doesn’t exist in the list.

For example:

System.out.println("Index of a: " + sentence.indexOf("a"));
System.out.println("Index of b: " + sentence.indexOf("b"));

Output:

Index of a: 2
Index of b: -1

Common operations and performance

The examples above cover some of the most common operations on List and Collection in general. However, one of the most important things to understand when working with data structures are the performance characteristics of each of them.

Different data structures offer features/characteristics that are meant for use in different scenarios. In the next sections, we’ll go into details about ArrayList, HashSet and HashMap and explain how they work behind the scenes and what performance can we expect out of each of these.

Understanding what data structure to use given a particular problem/scenario is an important task when developing software.