4.4. ArrayList

[9.4] Declare and use an ArrayList of a given type

In this section, I’ll cover how to use ArrayList, its commonly used methods, and the advantages it offers over an array.

The OCA Java SE 8 Programmer I exam covers only one class from the Java Collection API: ArrayList. The rest of the classes from the Java Collection API are covered in the OCP Java SE 8 Programmer II exam (exam number 1Z0-809). One of the reasons to include this class in the Java Associate exam could be how frequently this class is used by all Java programmers.

ArrayList is one of the most widely used classes from the Collections framework. It offers the best combination of features offered by an array and the List data structure. The most commonly used operations with a list are add items to a list, modify items in a list, delete items from a list, and iterate over the items.

One frequently asked question by Java developers is, “Why should I bother with an ArrayList when I can already store objects of the same type in an array?” The answer lies in the ease of use of an ArrayList. This is an important question, and this exam contains explicit questions on the practical reasons for using an ArrayList.

You can compare an ArrayList with a resizable array. As you know, once it’s created, you can’t increase or decrease the size of an array. On the other hand, an ArrayList automatically increases and decreases in size as elements are added to or removed from it. Also, unlike arrays, you don’t need to specify an initial size to create an ArrayList.

Let’s compare an ArrayList and an array with real-world objects. Just as a balloon can increase and decrease in size when it’s inflated or deflated, an ArrayList can increase or decrease in size as values are added to it or removed from it. One comparison is a cricket ball, because it has a predefined size. Once created, like an array, it can’t increase or decrease in size.

Here are a few more important properties of an ArrayList:

  • It implements the interface List.
  • It allows null values to be added to it.
  • It implements all list operations (add, modify, and delete values).
  • It allows duplicate values to be added to it.
  • It maintains its insertion order.
  • You can use either Iterator or ListIterator to iterate over the items of an ArrayList.
  • It supports generics, making it type safe. (You have to declare the type of the elements that should be added to an ArrayList with its declaration.)

4.4.1. Creating an ArrayList

The following example shows you how to create an ArrayList:

Package java.util isn’t implicitly imported into your class, which means that imports the class ArrayList in the class CreateArrayList defined previously. To create an ArrayList, you need to inform Java about the type of the objects that you want to store in this collection of objects. declares an ArrayList called myArrList, which can store String objects specified by the name of the class String between the angle brackets (<>). Note that the name String appears twice in the code at , once on the left side of the equal sign and once on the right. Do you think the second one seems redundant? Congratulations, Oracle agrees with you. Starting with Java version 7, you can omit the object type on the right side of the equal sign and create an ArrayList as follows:

Many developers still work with Java SE versions prior to version 7, so you’re likely to see some developers still using the older way of creating an ArrayList.

Take a look at what happens behind the scenes (in the Java source code) when you execute the previous statement to create an ArrayList. Because you didn’t pass any arguments to the constructor of class ArrayList, its no-argument constructor will execute. Examine the definition of the following no-argument constructor defined in the class ArrayList.java:

/**
  * Constructs an empty list with an initial capacity of ten.
  */
public ArrayList() {
    this(10);
}

Because you can use an ArrayList to store any type of Object, ArrayList defines an instance variable elementData of type Object[] to store all its individual elements. Following is a partial code listing from class ArrayList:

/**
  * The array buffer into which the elements of the ArrayList are stored.
  * The capacity of the ArrayList is the length of this array buffer.
  */
private transient Object[] elementData;

Figure 4.27 illustrates the variable elementData shown within an object of ArrayList.

Figure 4.27. Variable elementData shown within an object of ArrayList

Here’s the definition of the constructor from the class ArrayList (ArrayList.java), which initializes the previously defined instance variable, elementData:

/**
  * Constructs an empty list with the specified initial capacity.
  *
  * @param   initialCapacity   the initial capacity of the list
  * @exception IllegalArgumentException if the specified initial capacity
  *            is negative
  */
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
    this.elementData = new Object[initialCapacity];
}

Wait a minute. Did you notice that an ArrayList uses an array to store its individual elements? Does that make you wonder why on earth you would need another class if it uses a type (array, to be precise) that you already know how to work with? The simple answer is that you wouldn’t want to reinvent the wheel.

For an example, answer this question: to decode an image, which of the following options would you prefer?

  • Creating your own class using characters to decode the image
  • Using an existing class that offers the same functionality

Obviously, it makes sense to go with the second option. If you use an existing class that offers the same functionality, you get more benefits with less work. The same logic applies to ArrayList. It offers you all the benefits of using an array with none of the disadvantages. It looks and behaves like an expandable array that’s modifiable.

Note

An ArrayList uses an array to store its elements. It provides you with the functionality of a dynamic array.

I’ll cover how to add, modify, delete, and access the elements of an ArrayList in the following sections. Let’s start with adding elements to an ArrayList.

4.4.2. Adding elements to an ArrayList

Let’s begin by adding String objects to an ArrayList, as follows:

You can add a value to an ArrayList either at its end or at a specified position. adds elements at the end of list. adds an element to list at position 2. Please note that the first element of an ArrayList is stored at position 0. Hence, at the String literal value "three" will be inserted at position 2, which was occupied by the literal value "four". When a value is added to a place that’s already occupied by another element, the values shift by a place to accommodate the newly added value. In this case, the literal value "four" shifted to position 3 to make way for the literal value "three" (see figure 4.28).

Figure 4.28. Code that adds elements to the end of an ArrayList and at a specified position

Let’s see what happens behind the scenes in an ArrayList when you add an element to it. Here’s the definition of the method add from the class ArrayList:

/**
  * Appends the specified element to the end of this list.
  *
  * @param e element to be appended to this list
  * @return <tt>true</tt> (as specified by {@link Collection#add})
  */
public boolean add(E e) {
    ensureCapacity(size + 1);    // Create another array with
                                 // the increased capacity
                                 // and copy existing elements to it.

    elementData[size++] = e;     // Store the newly added variable
                                 // reference at the
                                 // end of the list.
    return true;
}

When you add an element to the end of the list, the ArrayList first checks whether its instance variable elementData has an empty slot at the end. If there’s an empty slot at its end, it stores the element at the first available empty slot. If no empty slots exist, the method ensureCapacity creates another array with a higher capacity and copies the existing values to this newly created array. It then copies the newly added value at the first available empty slot in the array.

When you add an element at a particular position, an ArrayList creates a new array (only if there’s not enough room left) and inserts all its elements at positions other than the position you specified. If there are any subsequent elements to the right of the position that you specified, it shifts them by one position. Then it adds the new element at the requested position.

Practical Tip

Understanding how and why a class works in a particular manner will take you a long way in regard to both the certification exam and your career. This understanding should help you retain the information for a longer time and help you answer questions in the certification exam that are designed to verify your practical knowledge of using this class. Finally, the internal workings of a class will enable you to make informed decisions on using a particular class at your workplace and writing efficient code.

4.4.3. Accessing elements of an ArrayList

Before we modify or delete the elements of an ArrayList, let’s see how to access them. To access the elements of an ArrayList, you can use get(), an enhanced for loop, Iterator, or ListIterator.

The following code accesses and prints all the elements of an ArrayList using the enhanced for loop (code to access elements is in bold):

The output of the previous code is as follows:

One
Two
Three
Four

defines the enhanced for loop to access all the elements of the myArrList.

Let’s look at how to use a ListIterator to loop through all the values of an ArrayList:

gets the iterator associated with ArrayList myArrList. calls the method has-Next on iterator to check whether more elements of myArrList exist. The method hasNext returns a boolean true value if more of its elements exist and false otherwise. calls the method next on iterator to get the next item from myArrList.

The previous code prints out the same results as the code that preceded it, the code that used an enhanced for loop to access ArrayList’s elements. A ListIterator doesn’t contain any reference to the current element of an ArrayList. ListIterator provides you with a method (hasNext) to check whether more elements exist for an ArrayList. If true, you can extract its next element using the method next().

Note that an ArrayList preserves the insertion order of its elements. ListIterator and the enhanced for loop will return to you the elements in the order in which you added them.

Exam Tip

An ArrayList preserves the order of insertion of its elements. Iterator, ListIterator, and the enhanced for loop will return the elements in the order in which they were added to the ArrayList. An iterator (Iterator or ListIterator) lets you remove elements as you iterate an ArrayList. It’s not possible to remove elements from an ArrayList while iterating it using a for loop.

4.4.4. Modifying the elements of an ArrayList

You can modify an ArrayList by either replacing an existing element in the ArrayList or modifying all of its existing values. The following code uses the set method to replace an element in an ArrayList:

The output of the previous code is as follows:

One
One and Half
Three

You can also modify the existing values of an ArrayList by accessing its individual elements. Because Strings are immutable, let’s try this with StringBuilder. Here’s the code:

The output of this code is as follows:

One3
Two3
Three5

accesses all the elements of myArrList and modifies the element value by appending its length to it. The modified value is printed by accessing myArrList elements again.

4.4.5. Deleting the elements of an ArrayList

ArrayList defines two methods to remove its elements, as follows:

  • remove(int index)—This method removes the element at the specified position in this list.
  • remove(Object o)—This method removes the first occurrence of the specified element from this list, if it’s present.

Let’s take a look at some code that uses these removal methods:

The output of the previous code is as follows:

One
Three
Four
One
Four

tries to remove the StringBuilder with the value "Four" from myArrList. The removal of the specified element fails because of the manner in which the object references are compared for equality. Objects are compared for equality using their equals() method, which isn’t overridden by the class StringBuilder. So two StringBuilder objects are equal if their object references (the variables that store them) point to the same object. You can always override the equals method in your own class to change this default behavior. The following is an example using the class MyPerson:

At , the method equals in the class MyPerson overrides the method equals in the class Object. It returns false if a null value is passed to this method. It returns true if an object of MyPerson is passed to it with a matching value for its instance variable name.

At , the method remove removes the element with the name Paul from myArr-List. As mentioned earlier, the method remove compares the objects for equality before removing it from ArrayList by calling the method equals.

When elements of an ArrayList are removed, the remaining elements are rearranged at their correct positions. This change is required to retrieve all the remaining elements at their correct new positions.

4.4.6. Other methods of ArrayList

Let’s briefly discuss the other important methods defined in ArrayList.

Adding multiple elements to an ArrayList

You can add multiple elements to an ArrayList from another ArrayList or any other class that’s a subclass of Collection by using the following overloaded versions of method addAll:

  • addAll(Collection<? extends E> c)
  • addAll(int index, Collection<? extends E> c)

The method addAll(Collection<? extends E> c) appends all the elements in the specified collection to the end of this list in the order in which they’re returned by the specified collection’s Iterator. If you aren’t familiar with generics, and the parameters of this method look scary to you, don’t worry—other classes from the Collection API aren’t on this exam.

Method addAll(int index, Collection<? extends E> c) inserts all the elements in the specified collection into this list, starting at the specified position.

In the following code example, all elements of ArrayList yourArrList are inserted into ArrayList myArrList, starting at position 1:

The output of the previous code is as follows:

One
Three
Four
Two

The elements of yourArrList aren’t removed from it. The objects that are stored in yourArrList can now be referred to from myArrList.

What happens if you modify the common object references in these lists, myArrList and yourArrList? We have two cases here: in the first one, you reassign the object reference using either of the lists. In this case, the value in the second list will remain unchanged. In the second case, you modify the internals of any of the common list elements—in this case, the change will be reflected in both of the lists.

Exam Tip

This is also one of the favorite topics of the exam authors. In the exam, you’re likely to encounter a question that adds the same object reference to multiple lists and then tests you on your understanding of the state of the same object and reference variable in all the lists. If you have any questions on this issue, please refer to the section on reference variables (section 2.3).

It’s time for our next Twist in the Tale exercise. Let’s modify some of the code that we’ve used in our previous examples and see how it affects the output (answers in the appendix).

Twist in the Tale 4.4

What is the output of the following code?

ArrayList<String> myArrList = new ArrayList<String>();
String one = "One";
String two = new String("Two");
myArrList.add(one);
myArrList.add(two);
ArrayList<String> yourArrList = myArrList;
one.replace("O", "B");
for (String val : myArrList)
    System.out.print(val + ":");
for (String val : yourArrList)
    System.out.print(val + ":");

  1. One:Two:One:Two:
  2. Bne:Two:Bne:Two:
  3. One:Two:Bne:Two:
  4. Bne:Two:One:Two:
Clearing ArrayList elements

You can remove all the ArrayList elements by calling clear on it. Here’s an example:

ArrayList<String> myArrList = new ArrayList<String>();
myArrList.add("One");
myArrList.add("Two");
myArrList.clear();
for (String val:myArrList)
    System.out.println(val);

The previous code won’t print out anything because there are no more elements in myArrList.

Accessing individual ArrayList elements

In this section, we’ll cover the following methods for accessing elements of an ArrayList:

  • get(int index)—This method returns the element at the specified position in this list.
  • size()—This method returns the number of elements in this list.
  • contains(Object o)—This method returns true if this list contains the specified element.
  • indexOf(Object o)—This method returns the index of the first occurrence of the specified element in this list, or –1 if this list doesn’t contain the element.
  • lastIndexOf(Object o)—This method returns the index of the last occurrence of the specified element in this list, or –1 if this list doesn’t contain the element.

You can retrieve an object at a particular position in ArrayList and determine its size as follows:

Behind the scenes, the method get will check whether the requested position exists in the ArrayList by comparing it with the array’s size. If the requested element isn’t within the range, the get method throws a java.lang.IndexOutOfBoundsException error at runtime.

All the remaining three methods—contains, indexOf, and lastIndexOf—require you to have an unambiguous and strong understanding of how to determine the equality of objects. ArrayList stores objects, and these three methods will compare the values that you pass to these methods with all the elements of the ArrayList.

By default, objects are considered equal if they are referred to by the same variable (the String class is an exception with its pool of String objects). If you want to compare objects by their state (values of the instance variable), override the equals method in that class. I’ve already demonstrated the difference in how equality of objects of a class is determined, when the class overrides its equals method and when it doesn’t, in section 4.4.5 with an overridden equals method in the class MyPerson.

Let’s see the usage of all these methods:

The output of the previous code is as follows:

false
true
-1
1
-1
2

Take a look at the output of the same code using a list of MyPerson objects that has overridden the equals method. First, here’s the definition of the class MyPerson:

The definition of the class MiscMethodsArrayList4 follows:

As you can see from the output of the preceding code, equality of the objects of the class MyPerson is determined by the rules defined in its equals method. Two objects of the class MyPerson with the same value for its instance variable name are considered to be equal. myArrList stores objects of the class MyPerson. To find a target object, myArrList will rely on the output given by the equals method of the class MyPerson; it won’t compare the object references of the stored and target objects.

Exam Tip

An ArrayList can store duplicate object values.

Cloning an ArrayList

The method clone defined in the class ArrayList returns a shallow copy of this ArrayList instance. “Shallow copy” means that this method creates a new instance of the ArrayList object to be cloned. Its element references are copied, but the objects themselves are not.

Here’s an example:

Let’s go through the previous code:

  • assigns the object referred to by myArrList to assignedArrList. The variables myArrList and assignedArrList now refer to the same object.
  • assigns a copy of the object referred to by myArrList to clonedArrList. The variables myArrList and clonedArrList refer to different objects. Because the method clone returns a value of the type Object, it’s cast to ArrayList<String-Builder> to assign it to clonedArrList (don’t worry if you can’t follow this line—casting is covered in chapter 6).
  • prints true because myArrList and assignedArrList refer to the same object.
  • prints false because myArrList and clonedArrList refer to separate objects, because the method clone creates and returns a new object of ArrayList (but with the same list members).
  • proves that the method clone didn’t copy the elements of myArrList. All the variable references myArrVal, AssignedArrVal, and clonedArrVal refer to the same objects.
  • Hence, both and print true.
Creating an array from an ArrayList

You can use the method toArray to return an array containing all the elements in an ArrayList in sequence from the first to the last element. As mentioned earlier in this chapter (refer to figure 4.27 in section 4.4.1), an ArrayList uses a private variable, elementData (an array), to store its own values. Method toArray doesn’t return a reference to this array. It creates a new array, copies the elements of the ArrayList to it, and then returns it.

Now comes the tricky part. No references to the returned array, which is itself an object, are maintained by the ArrayList. But the references to the individual ArrayList elements are copied to the returned array and are still referred to by the ArrayList.

This implies that if you modify the returned array by, say, swapping the position of its elements or by assigning new objects to its elements, the elements of ArrayList won’t be affected. But if you modify the state of (mutable) elements of the returned array, then the modified state of elements will be reflected in the ArrayList.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.134.17