We are going to introduce a new data structure called ArrayList that solves many of the problems with arrays. It is your new best friend.
You’ve learned about arrays, which are useful for storing multiple items of the same type.
But arrays have some limitations that can make them frustrating to work with:
-
Fixed size - Once you create an array, you can’t make it bigger or smaller
-
No built-in methods - You have to write your own code to search, sort, or manipulate the data
-
Manual management - You need to keep track of how many elements you’ve actually used
Java provides a much better solution called ArrayList that solves all these problems. ArrayList is part of Java’s Collections Framework and is what most professional Java programmers use instead of arrays for storing lists of items.
And honestly, you should just use ArrayLists, because all the Java Pros do.
Let’s see the difference with a practical example. Imagine you’re building a program to keep track of student names in a class:
jshell> // With arrays - lots of manual work and limitations
...> String[] studentNames = new String[10]; // Fixed size of 10
studentNames ==> String[10] { null, null, null, null, null, ... }
jshell> int studentCount = 0; // We have to manually track how many we've added
studentCount ==> 0
jshell> // Adding students manually
...> studentNames[studentCount] = "Alice";
studentNames[0] ==> "Alice"
jshell> studentCount++;
studentCount ==> 1
jshell> studentNames[studentCount] = "Bob";
studentNames[1] ==> "Bob"
jshell> studentCount++;
studentCount ==> 2
jshell> // What if we get an 11th student? We can't add them!
...> // What if we want to remove a student? Complex shuffling required!
...>
...> System.out.println("Current students: " + studentCount);
Current students: 2
jshell> // To print all students, we need a loop
...> for (int i = 0; i < studentCount; i++) {
...> System.out.println("Student " + (i + 1) + ": " + studentNames[i]);
...> }
Student 1: Alice
Student 2: Bobjshell> import java.util.ArrayList;
jshell> // With ArrayList - dynamic and easy to use
...> ArrayList<String> students = new ArrayList<>();
students ==> []
jshell> // Adding students is simple
...> students.add("Alice");
$1 ==> true
jshell> students.add("Bob");
$2 ==> true
jshell> students.add("Charlie");
$3 ==> true
jshell> students.add("Diana"); // No limit! It grows automatically
$4 ==> true
jshell> // Size is automatic
...> System.out.println("Number of students: " + students.size());
Number of students: 4
jshell> // Easy to print all students
...> System.out.println("All students: " + students);
All students: [Alice, Bob, Charlie, Diana]
jshell> // Easy to print with a loop
...> for (String student : students) {
...> System.out.println("Student: " + student);
...> }
Student: Alice
Student: Bob
Student: Charlie
Student: DianaSee how much cleaner and easier ArrayList is to work with!
jshell> // Creating different types of ArrayLists
...> ArrayList<String> names = new ArrayList<>(); // For strings
names ==> []
jshell> ArrayList<Integer> numbers = new ArrayList<>(); // For integers
numbers ==> []
jshell> ArrayList<Double> prices = new ArrayList<>(); // For decimal numbers
prices ==> []
jshell> // You can also initialize with values
...> ArrayList<String> colors = new ArrayList<>();
colors ==> []
jshell> colors.add("red");
$5 ==> true
jshell> colors.add("green");
$6 ==> true
jshell> colors.add("blue");
$7 ==> true
jshell> System.out.println("Colors: " + colors);
Colors: [red, green, blue]Note the <String>, <Integer>, etc. - these are called generics and they tell Java what type of objects the ArrayList will hold. This prevents you from accidentally mixing different types.
jshell> ArrayList<String> fruits = new ArrayList<>();
fruits ==> []
jshell> // Add to the end
...> fruits.add("apple");
$8 ==> true
jshell> fruits.add("banana");
$9 ==> true
jshell> fruits.add("cherry");
$10 ==> true
jshell> System.out.println("Fruits: " + fruits);
Fruits: [apple, banana, cherry]
jshell> // Add at a specific position
...> fruits.add(1, "blueberry"); // Insert at index 1
jshell> System.out.println("After insertion: " + fruits);
After insertion: [apple, blueberry, banana, cherry]
jshell> // Add at the beginning
...> fruits.add(0, "apricot");
jshell> System.out.println("After adding at start: " + fruits);
After adding at start: [apricot, apple, blueberry, banana, cherry]jshell> // Get element by index (like arrays)
...> String firstFruit = fruits.get(0);
firstFruit ==> "apricot"
jshell> String lastFruit = fruits.get(fruits.size() - 1);
lastFruit ==> "cherry"
jshell> System.out.println("First: " + firstFruit + ", Last: " + lastFruit);
First: apricot, Last: cherry
jshell> // Check if ArrayList contains something
...> boolean hasApple = fruits.contains("apple");
hasApple ==> true
jshell> boolean hasOrange = fruits.contains("orange");
hasOrange ==> false
jshell> System.out.println("Has apple: " + hasApple + ", Has orange: " + hasOrange);
Has apple: true, Has orange: false
jshell> // Find the index of an element
...> int appleIndex = fruits.indexOf("apple");
appleIndex ==> 1
jshell> System.out.println("Apple is at index: " + appleIndex);
Apple is at index: 1jshell> System.out.println("Before removal: " + fruits);
Before removal: [apricot, apple, blueberry, banana, cherry]
jshell> // Remove by value
...> boolean removed = fruits.remove("banana");
removed ==> true
jshell> System.out.println("After removing banana: " + fruits);
After removing banana: [apricot, apple, blueberry, cherry]
jshell> // Remove by index
...> String removedFruit = fruits.remove(2); // Remove at index 2
removedFruit ==> "blueberry"
jshell> System.out.println("Removed: " + removedFruit);
Removed: blueberry
jshell> System.out.println("After removing by index: " + fruits);
After removing by index: [apricot, apple, cherry]
jshell> // Clear all elements
...> ArrayList<String> temp = new ArrayList<>();
temp ==> []
jshell> temp.add("test1");
$11 ==> true
jshell> temp.add("test2");
$12 ==> true
jshell> System.out.println("Before clear: " + temp);
Before clear: [test1, test2]
jshell> temp.clear();
jshell> System.out.println("After clear: " + temp);
After clear: []jshell> System.out.println("Current fruits: " + fruits);
Current fruits: [apricot, apple, cherry]
jshell> // Change an element at a specific index
...> String oldValue = fruits.set(1, "avocado"); // Replace "apple" with "avocado"
oldValue ==> "apple"
jshell> System.out.println("Replaced '" + oldValue + "' with 'avocado'");
Replaced 'apple' with 'avocado'
jshell> System.out.println("Updated fruits: " + fruits);
Updated fruits: [apricot, avocado, cherry]Let’s build a practical example that shows why ArrayList is so much better than arrays:
jshell> class GradeTracker {
...> private ArrayList<Double> grades;
...> private String studentName;
...>
...> public GradeTracker(String name) {
...> this.studentName = name;
...> this.grades = new ArrayList<>();
...> }
...>
...> public void addGrade(double grade) {
...> if (grade >= 0 && grade <= 100) {
...> grades.add(grade);
...> System.out.println("Added grade " + grade + " for " + studentName);
...> } else {
...> System.out.println("Invalid grade: " + grade + ". Must be 0-100.");
...> }
...> }
...>
...> public void removeGrade(double grade) {
...> if (grades.remove(grade)) { // remove() returns true if found and removed
...> System.out.println("Removed grade " + grade + " for " + studentName);
...> } else {
...> System.out.println("Grade " + grade + " not found for " + studentName);
...> }
...> }
...>
...> public double getAverage() {
...> if (grades.isEmpty()) {
...> return 0.0;
...> }
...>
...> double sum = 0.0;
...> for (double grade : grades) {
...> sum += grade;
...> }
...> return sum / grades.size();
...> }
...>
...> public void printReport() {
...> System.out.println("\n=== Grade Report for " + studentName + " ===");
...> System.out.println("All grades: " + grades);
...> System.out.println("Number of grades: " + grades.size());
...> System.out.println("Average: " + String.format("%.2f", getAverage()));
...>
...> if (!grades.isEmpty()) {
...> double highest = grades.get(0);
...> double lowest = grades.get(0);
...>
...> for (double grade : grades) {
...> if (grade > highest) highest = grade;
...> if (grade < lowest) lowest = grade;
...> }
...>
...> System.out.println("Highest grade: " + highest);
...> System.out.println("Lowest grade: " + lowest);
...> }
...> System.out.println("========================\n");
...> }
...> }
| created class GradeTracker
jshell> // Test the grade tracker
...> GradeTracker alice = new GradeTracker("Alice");
alice ==> GradeTracker@...
jshell> alice.addGrade(85.5);
Added grade 85.5 for Alice
jshell> alice.addGrade(92.0);
Added grade 92.0 for Alice
jshell> alice.addGrade(78.5);
Added grade 78.5 for Alice
jshell> alice.addGrade(88.0);
Added grade 88.0 for Alice
jshell> alice.printReport();
=== Grade Report for Alice ===
All grades: [85.5, 92.0, 78.5, 88.0]
Number of grades: 4
Average: 86.00
Highest grade: 92.0
Lowest grade: 78.5
========================
jshell> // Easy to add more grades - no size limit!
...> alice.addGrade(95.0);
Added grade 95.0 for Alice
jshell> alice.addGrade(82.5);
Added grade 82.5 for Alice
jshell> alice.printReport();
=== Grade Report for Alice ===
All grades: [85.5, 92.0, 78.5, 88.0, 95.0, 82.5]
Number of grades: 6
Average: 86.92
Highest grade: 95.0
Lowest grade: 78.5
========================
jshell> // Easy to remove a grade if there was an error
...> alice.removeGrade(78.5);
Removed grade 78.5 for Alice
jshell> alice.printReport();
=== Grade Report for Alice ===
All grades: [85.5, 92.0, 88.0, 95.0, 82.5]
Number of grades: 5
Average: 88.60
Highest grade: 95.0
Lowest grade: 82.5
========================Try to imagine writing this same program with arrays - you’d need to manually resize arrays, shift elements when removing items, and keep track of how many grades you’ve actually stored. With ArrayList, it’s all handled automatically!
You can create ArrayLists for any type of object:
jshell> ArrayList<Integer> ages = new ArrayList<>();
ages ==> []
jshell> ages.add(25);
$13 ==> true
jshell> ages.add(30);
$14 ==> true
jshell> ages.add(22);
$15 ==> true
jshell> ages.add(35);
$16 ==> true
jshell> System.out.println("Ages: " + ages);
Ages: [25, 30, 22, 35]
jshell> // Find the average age
...> double totalAge = 0;
totalAge ==> 0.0
jshell> for (int age : ages) {
...> totalAge += age;
...> }
jshell> double averageAge = totalAge / ages.size();
averageAge ==> 28.0
jshell> System.out.println("Average age: " + averageAge);
Average age: 28.0jshell> ArrayList<Boolean> testResults = new ArrayList<>();
testResults ==> []
jshell> testResults.add(true); // Test 1: passed
$17 ==> true
jshell> testResults.add(false); // Test 2: failed
$18 ==> true
jshell> testResults.add(true); // Test 3: passed
$19 ==> true
jshell> testResults.add(true); // Test 4: passed
$20 ==> true
jshell> // Count how many tests passed
...> int passedCount = 0;
passedCount ==> 0
jshell> for (boolean result : testResults) {
...> if (result) {
...> passedCount++;
...> }
...> }
jshell> System.out.println("Tests passed: " + passedCount + " out of " + testResults.size());
Tests passed: 3 out of 4ArrayList works great with Java’s built-in sorting and searching methods:
jshell> import java.util.Collections;
jshell> ArrayList<String> names = new ArrayList<>();
names ==> []
jshell> names.add("Charlie");
$21 ==> true
jshell> names.add("Alice");
$22 ==> true
jshell> names.add("Bob");
$23 ==> true
jshell> names.add("Diana");
$24 ==> true
jshell> System.out.println("Before sorting: " + names);
Before sorting: [Charlie, Alice, Bob, Diana]
jshell> // Sort alphabetically
...> Collections.sort(names);
jshell> System.out.println("After sorting: " + names);
After sorting: [Alice, Bob, Charlie, Diana]
jshell> // Shuffle randomly
...> Collections.shuffle(names);
jshell> System.out.println("After shuffling: " + names);
After shuffling: [Bob, Diana, Alice, Charlie]
jshell> // Sort numbers
...> ArrayList<Integer> numbers = new ArrayList<>();
numbers ==> []
jshell> numbers.add(45);
$25 ==> true
jshell> numbers.add(12);
$26 ==> true
jshell> numbers.add(78);
$27 ==> true
jshell> numbers.add(23);
$28 ==> true
jshell> System.out.println("Before sorting: " + numbers);
Before sorting: [45, 12, 78, 23]
jshell> Collections.sort(numbers);
jshell> System.out.println("After sorting: " + numbers);
After sorting: [12, 23, 45, 78]Sometimes you might need to work with both arrays and ArrayLists in the same program:
jshell> import java.util.Arrays;
jshell> // Convert array to ArrayList
...> String[] cityArray = {"New York", "Los Angeles", "Chicago", "Houston"};
cityArray ==> String[4] { "New York", "Los Angeles", "Chicago", "Houston" }
jshell> ArrayList<String> cities = new ArrayList<>(Arrays.asList(cityArray));
cities ==> [New York, Los Angeles, Chicago, Houston]
jshell> System.out.println("ArrayList from array: " + cities);
ArrayList from array: [New York, Los Angeles, Chicago, Houston]
jshell> // Now we can use ArrayList methods
...> cities.add("Phoenix");
$29 ==> true
jshell> cities.remove("Chicago");
$30 ==> true
jshell> System.out.println("Modified ArrayList: " + cities);
Modified ArrayList: [New York, Los Angeles, Houston, Phoenix]jshell> // Convert ArrayList back to array
...> String[] newCityArray = cities.toArray(new String[0]);
newCityArray ==> String[4] { "New York", "Los Angeles", "Houston", "Phoenix" }
jshell> System.out.println("Array from ArrayList: " + Arrays.toString(newCityArray));
Array from ArrayList: [New York, Los Angeles, Houston, Phoenix]Use ArrayList when:
- You don’t know how many elements you’ll have
- You need to add or remove elements frequently
- You want convenient methods like contains(), indexOf(), etc.
- You’re building most types of applications (this is the common case)
Use arrays when: - You have a fixed number of elements that won’t change - You need maximum performance for mathematical calculations - You’re working with primitive types and need to save memory - You’re interfacing with code that specifically requires arrays
Here’s a quick reference of the most useful ArrayList methods:
jshell> // Creating and basic operations
...> ArrayList<String> list = new ArrayList<>();
list ==> []
jshell> list.add("item"); // Add to end
$31 ==> true
jshell> list.add(0, "first"); // Add at specific index
jshell> list.get(0); // Get element at index
$32 ==> "first"
jshell> list.set(0, "new first"); // Replace element at index
$33 ==> "first"
jshell> list.remove("item"); // Remove by value
$34 ==> true
jshell> list.remove(0); // Remove by index
$35 ==> "new first"
jshell> // Information methods
...> list.add("test");
$36 ==> true
jshell> list.size(); // Number of elements
$37 ==> 1
jshell> list.isEmpty(); // Check if empty
$38 ==> false
jshell> list.contains("test"); // Check if contains value
$39 ==> true
jshell> list.indexOf("test"); // Find index of value
$40 ==> 0
jshell> // Bulk operations
...> list.clear(); // Remove all elements
jshell> list.isEmpty();
$41 ==> trueTry building this shopping list manager to practice using ArrayList:
jshell> class ShoppingList {
...> private ArrayList<String> items;
...>
...> public ShoppingList() {
...> items = new ArrayList<>();
...> }
...>
...> public void addItem(String item) {
...> if (!items.contains(item)) {
...> items.add(item);
...> System.out.println("Added: " + item);
...> } else {
...> System.out.println(item + " is already on the list");
...> }
...> }
...>
...> public void removeItem(String item) {
...> if (items.remove(item)) {
...> System.out.println("Removed: " + item);
...> } else {
...> System.out.println(item + " was not on the list");
...> }
...> }
...>
...> public void showList() {
...> if (items.isEmpty()) {
...> System.out.println("Shopping list is empty");
...> } else {
...> System.out.println("\nShopping List:");
...> for (int i = 0; i < items.size(); i++) {
...> System.out.println((i + 1) + ". " + items.get(i));
...> }
...> System.out.println("Total items: " + items.size() + "\n");
...> }
...> }
...>
...> public boolean hasItem(String item) {
...> return items.contains(item);
...> }
...> }
| created class ShoppingList
jshell> // Test the shopping list
...> ShoppingList groceries = new ShoppingList();
groceries ==> ShoppingList@...
jshell> groceries.addItem("milk");
Added: milk
jshell> groceries.addItem("bread");
Added: bread
jshell> groceries.addItem("eggs");
Added: eggs
jshell> groceries.addItem("milk"); // Duplicate
milk is already on the list
jshell> groceries.showList();
Shopping List:
1. milk
2. bread
3. eggs
Total items: 3
jshell> groceries.removeItem("bread");
Removed: bread
jshell> groceries.showList();
Shopping List:
1. milk
2. eggs
Total items: 2
jshell> System.out.println("Need milk? " + groceries.hasItem("milk"));
Need milk? true-
ArrayList is usually better than arrays for most programming tasks
-
Dynamic size - ArrayLists grow and shrink automatically as needed
-
Rich functionality - Many built-in methods for common operations
-
Type safety - Generics (
<String>,<Integer>) prevent type errors -
Easy to use - Adding, removing, and accessing elements is straightforward
-
Integrates well - Works seamlessly with other Java features and libraries
ArrayList is one of the most commonly used classes in Java programming. Once you get comfortable with it, you’ll find yourself using it constantly instead of arrays. It makes your code cleaner, safer, and much easier to maintain.
Most professional Java code uses ArrayList (and other Collections) rather than basic arrays, so learning ArrayList well is essential for becoming a competent Java programmer.