Skip to main content

Section 5.7 Working with ArrayList

Subsection 5.7.1 Introduction & Motivation

Arrays in Java are fundamental building blocks for data storage, but they also have a notable limitation: fixed size. Once declared, you cannot expand or shrink them on the fly. In practical scenarios, we often need a container that can grow or shrink as items are inserted or removed—like storing file lines, dynamic lists of game entities, or user inputs of unpredictable length.
The ArrayList class provides precisely this flexibility. It is part of Java’s java.util package and serves as one of the most widely used data structures for in-memory collections of objects. In this lesson, we will explore:

Subsection 5.7.2 Basic Creation & Usage

An ArrayList<T> is a List implementation designed to store objects of type T. You create it by specifying the element type in angle brackets, for example, ArrayList<String>. This approach enforces compile-time type checks, preventing accidental mixing of types.
Theme 1: Basic Setup & Indexing Strategies
  • Creating: Use new ArrayList<>() or new ArrayList<String>() to instantiate with a specific type.
  • Adding/Accessing: Use add to append, size() to check how many elements exist, and get(index) to retrieve a specific element.
Common Pitfalls to Avoid:
  • IndexOutOfBoundsException: For instance, if you invoke get(5) but you only have 2 elements, you’ll trigger an exception. Always check size() first!
  • Null Items vs. Null ArrayList: Although names.add(null) is valid (though uncommon). If the names object itself is null, any method call triggers a NullPointerException.

Subsection 5.7.3 Inserting & Removing Elements

In addition to appending with add(...), ArrayList also supports positional inserts, as well as multiple ways to remove elements. We’ll organize them below:
Theme 2: Insertions & Removals in ArrayList
  • add(index, element) — Inserts at a specific index, shifting subsequent elements to the right within the list.
  • remove(index) — Removes an item by index, causing subsequent elements to shift left.
  • remove(Object o) — Removes the first occurrence of a matching object (as defined by equals), returning true if found.
  • clear() — Empties the entire list of all elements.
Key Pitfalls & Additional Notes:
  • Index Boundaries: add(index, element) or remove(index) must reference valid positions (0 <= index <= size()).
  • Object Removal Ambiguities: When a list contains duplicates, remove("test") only takes out the first match. You can loop or employ alternative methods to remove all occurrences if needed.
  • Shifting Elements: Insertions or removals in the middle of an ArrayList can be inefficient for large lists because the rest of the elements must shift. For large-scale data scenarios, a linked structure might outperform ArrayList at random inserts. However, for small to medium lists, ArrayList usually performs adequately.

Subsection 5.7.4 Iterating & Searching for Elements

Reading and searching within an ArrayList is a routine task in Java development. Fortunately, Java provides several ways to iterate:
Theme 3: Strategies for Iteration & Basic Searching
  • For-each loop: for (String x : list). Very concise, but you lose direct access to the index.
  • Indexed for loop: for (int i = 0; i < list.size(); i++). Allows list.get(i) for direct index-based access.
  • contains(...): Verifies if the list contains an element matching equals criteria.
  • indexOf(...): Returns the first index of a matching element, or -1 if no match is found.
Common Pitfalls and Warnings:
  • Concurrent Modification: A "concurrent modification" happens when you try to change a list at the same time you’re reading through it. For example, if you use a for-each loop to go through a list and try to remove items during that loop, Java will throw a ConcurrentModificationException. This is because the for-each loop needs the list to stay unchanged while it’s iterating - removing items partway through would make Java lose track of where it is in the list. (We’ll learn safe ways to remove items using iterators in later sections.)
  • Using "==" Instead of equals: When dealing with objects, contains depends on equals. If you haven’t overridden equals in your custom class, the default behavior might be unexpected.

Subsection 5.7.5 ArrayList & Wrapper Types (Auto-Boxing)

ArrayList exclusively stores **objects**. But what if you need a list of primitive int? Java’s type system forces you to use a wrapper class such as Integer to achieve this.
Fortunately, Java seamlessly converts between int and Integer whenever you invoke list.add(5) or retrieve an element. This mechanism is called auto-boxing and unboxing.
Theme 4: Handling Primitives in an ArrayList
  • auto-boxing: The process of wrapping (or "boxing up") a primitive value inside its corresponding wrapper object - like putting an int value into an Integer "box" or a double into a Double "box". Java does this automatically when needed.
  • auto-unboxing: The reverse process: taking the primitive value back out of its wrapper object "box" (e.g., extracting the int from an Integer). Java handles this automatically too.
  • Performance & Pitfalls: Creating wrapper objects and boxing/unboxing values requires extra memory and processing time. In tight loops or with large datasets, these small costs can add up significantly. For better performance with large amounts of numeric data, consider using IntStream or arrays of primitives instead.

Subsection 5.7.6 Conversions & Additional Utility Methods

Beyond fundamental operations (add, remove, get, contains), ArrayList provides several other helpful utilities. We’ll group them here as the "miscellaneous yet important" category.
Theme 5: Conversion & Utility Methods
  • toArray(): Converts an ArrayList into a standard array. For instance: String[] arr = list.toArray(new String[0]).
  • subList(from, to): Returns a view of a specified portion of the list (from index from up to to - 1). Changes in the sublist will reflect in the original list, so caution is advised.
  • sort(Comparator): In Java 8 or later, you can call list.sort(Comparator.naturalOrder()) or provide a custom comparator for advanced sorting needs. (Alternatively, use Collections.sort(list).)
Pitfalls & Important Notes:
  • Sublist’s Link: fruits.subList(...) creates a "window" into the original list - it doesn’t create a new independent list. Think of it like looking through a window into a room - if you rearrange furniture (modify elements) through the window, the room itself changes. Any modifications made through the sublist (adding, removing, or changing elements) will directly affect the original list at those positions. If you need a separate, independent copy that can be modified without affecting the original, use new ArrayList<>(subList) to create a new list with the same elements.
  • Sorting: Sorting means arranging elements in a specific order (like alphabetical order for text, or numerical order for numbers). When you call list.sort(null), Java will try to sort the elements in their "natural" order - for example, text will be sorted alphabetically, and numbers from smallest to largest. However, for this to work, the elements must know how to be compared to each other. Built-in types like String and Integer already know how to be compared, but for custom objects you’ll need to teach Java how to compare them. We’ll learn more about this when we cover the Comparable interface in a later chapter.

Subsection 5.7.7 Conclusion

In this lesson, we examined the fundamentals of ArrayList, starting with creation and basic additions, moving on to iteration, searching, and specialized methods. This knowledge will be invaluable in any project that needs dynamic lists of objects. Keep these key takeaways in mind:
  • Flexibility vs. Performance: ArrayList is a reliable default, but be mindful that frequent insertions or removals in the middle can be costly. For smaller lists, it’s usually fine.
  • Index Checking: size() helps ensure safe indexing. Indexing errors lead to runtime exceptions, not compile-time warnings.
  • Auto-Boxing: Convenient for handling primitives within an ArrayList; however, be mindful of performance impacts in large loops or data sets.
  • Utilities: Methods like contains, indexOf, sort, and toArray can be time-savers. Just remember subList() is a "live" window, not an independent copy.
Mastering ArrayList paves the way for exploring more advanced collections in Java—such as LinkedList, HashMap, or TreeSet. If you keep in mind how to handle insertions/removals, iteration, and various edge cases, you’ll find ArrayList an indispensable data structure for day-to-day development in Java.

Subsection 5.7.8 Check Your Understanding

Exercises Exercises

1. Multiple-Choice: Creating & Basic Methods.
Which of the following statements about creating and using an ArrayList is correct?
  • You can declare it as ArrayList<String> list = new ArrayList<String>();, then use list.add(...) and list.get(...) to manage elements.
  • Exactly! That’s the standard approach: specify the type parameter <String> and call add/get to manipulate elements.
  • You must import java.util.ArrayList, but you cannot add elements using add; you must use list[index] = ... to insert items.
  • No. ArrayList doesn’t use the [] syntax for assignment; we call add or set instead.
  • It’s impossible to store strings in an ArrayList—only numeric data is allowed by default.
  • No. ArrayList is generic and can store any type of object, including strings.
  • If list has been instantiated but is empty, calling list.get(0) will return null instead of throwing an exception.
  • No. If it’s empty, get(0) throws an IndexOutOfBoundsException, not null.
2. Multiple-Choice: Insertions & Removals.
You have ArrayList<String> tasks = new ArrayList<>() with elements {"Wash dishes", "Go running", "Buy milk"}. If you call tasks.add(1, "Pay bills"), then tasks.remove("Buy milk"), what is the final content of tasks?
  • {"Wash dishes", "Pay bills", "Buy milk"}
  • Not quite—remember we remove "Buy milk" after adding "Pay bills".
  • {"Pay bills", "Wash dishes", "Go running"}
  • No. Inserting at index 1 puts "Pay bills" between "Wash dishes" and "Go running."
  • {"Wash dishes", "Pay bills", "Go running"}
  • Correct! After insertion at index 1, the list is {"Wash dishes", "Pay bills", "Go running", "Buy milk"}. Removing "Buy milk" leaves {"Wash dishes", "Pay bills", "Go running"}.
  • {"Wash dishes", "Go running"}
  • No. That would happen if we removed "Pay bills" instead of "Buy milk."
4. Multiple-Choice: Auto-Boxing & Wrapper Types.
Which statement about storing integers in an ArrayList<Integer> is correct?
  • You must call Integer.valueOf(...) manually before adding an int to the list.
  • No. Java does that step implicitly (auto-boxing) for convenience.
  • new ArrayList<Integer>() actually stores int primitives directly; no objects are created.
  • No. ArrayList<Integer> always holds Integer objects under the hood.
  • Java automatically converts int to Integer (auto-boxing) and vice versa (auto-unboxing) when you add or retrieve elements.
  • Exactly! This is the essence of auto-boxing and unboxing for int.
  • If you add an int to the list and retrieve it as Integer, you must cast it to (Integer) first to avoid compile errors.
  • No. The generics system and auto-unboxing handle the conversions seamlessly, so no cast is needed.
5. Multiple-Choice: subList(...) Behavior.
What happens if you obtain a sublist with List<String> sub = list.subList(1, 3) and then modify the sublist?
  • It creates a fully independent copy, so the original list remains unchanged.
  • No. That’s only true if you explicitly create a new ArrayList from the sublist, e.g. new ArrayList(subList(...)).
  • It throws an UnsupportedOperationException if you try to remove items from sub.
  • No. A sublist is modifiable by default if the parent list is modifiable. However, changes reflect in the parent list.
  • It triggers an IndexOutOfBoundsException because sublists are read-only by default.
  • No. Sublists are usually read-write, not read-only. You only get bounds errors if you exceed valid indices.
  • The changes are reflected in the original list, because subList returns a view into the same underlying data.
  • Correct! Modifications in the sublist also affect the parent list. They share the same data region.
6. Multiple-Choice: Sorting & Utility Methods.
Consider you have ArrayList<String> fruits = new ArrayList<>(List.of("Orange", "Apple", "Banana")); and you call fruits.sort(null);. Which statement is true regarding the result?
  • The list is reversed: ["Banana", "Apple", "Orange"] because passing null triggers reverse order by default.
  • No. Null means "use natural order," not reversed.
  • The list is now sorted in ascending (alphabetical) order: ["Apple", "Banana", "Orange"].
  • Yes. Passing null to sort means use natural order, which for strings is alphabetical.
  • It throws a NullPointerException because sort(null) is invalid.
  • No. If you pass null for the comparator, Java uses the elements’ natural ordering.
  • The sort method does nothing, leaving the list in the original insertion order.
  • No. sort(null) definitely sorts by natural order, not a no-op.
7. Short-Answer: Removals in a Loop.
Why might removing elements from an ArrayList inside a for-each loop lead to a ConcurrentModificationException, and what is one strategy to remove items safely in such situations?
Answer.
iterator.remove()
8. Short-Answer: Performance Considerations.
In terms of performance, when might a linked-based collection (like LinkedList) be more appropriate than ArrayList? Give a scenario based on what you learned.
Answer.
You have attempted of activities on this page.