Mastering ArrayLists: The Cornerstone Java Collection

ArrayLists are found in nearly 70% of all Java applications, forming the backbone of app data structures to provide dynamic, resizeable arrays. Master ArrayLists and level up your Java fluency!

This comprehensive 2800 word guide serves up everything a budding or seasoned Java dev needs to know about ArrayLists – the most essential collection type with over 15 years of community refinement since JDK 1.2.

We‘ll cover all aspects of ArrayLists from internal expansion algorithms to thread safety, performance tradeoffs and modern stream usage. Buckle up for the definitive ArrayLists journey!

ArrayList Superpowers

Before diving into the rough and tumble ArrayLists details, let‘s highlight why they are beloved by Java developers:

  • Automatic resizing without manual array reallocation
  • O(1) get/set access with index lookups
  • Inserting/deleting without shifting elements
  • Familiar array-like API paired with helpful methods
  • Stores object references unlike primitive arrays
  • Backbone of most Java collection processing

Java arrays require pre-allocation with fixed lengths. ArrayLists solve this via:

  • Default constructor starts with empty array
  • Adds trigger expansion to 1.5x size
  • New array copied via System.arraycopy()
  • Old array eligible for garbage collection

This enables simplified coding without managing capacities. We scale vast datasets effortlessly now!

Production Java apps lean on ArrayLists for:

  • In-memory caching layers
  • Search/ranking result sets
  • Analytics event queuing
  • History/audit logging

Their versatility enables usage across web, mobile, and backend systems.

Now that your ArrayList appetite is whetted, let‘s uncover more of their secrets!

Declaration and Initialization

ArrayLists reside in java.util so import that package:

import java.util.ArrayList;

Then declare an ArrayList specifying the element type in angle brackets:

ArrayList<String> fruits = new ArrayList<>();

The key considerations are:

  • The element type (String here)
  • The initial capacity (default is 10)

For primitives like int, wrap them in classes like Integer.

Initialize based on access patterns:

ArrayList<Integer> nums = new ArrayList<>(); // starts at 10
ArrayList<Integer> nums = new ArrayList<>(100); // starts at 100

Higher initial capacities minimize resizing.

Inside ArrayLists

Behind the straightforward ArrayList interface lies intricate resizing algorithms in the JDK:

  • As elements added, capacity crosses thresholds
  • New array allocated at 1.5x size
  • Elements copied via native System.arraycopy()
  • Old array eligible for garbage collection

This underlying array doubling enables O(1) amortized time inserts and deletions.

ArrayList data structure

Figure 1: ArrayLists use dynamically grown arrays

ArrayLists Avoid the manual array reallocation burden. The latest OpenJDK code has 15+ years of tuning since Java 1.2!

Now that you know about the inner workings of ArrayLists, let‘s leverage them in code.

Adding Elements

Elements are added using the add() method:

fruits.add("Apple"); //append to end 

fruits.add(0, "Banana"); //insert at index 0  

You can also initialize via:

String[] elements = {"Apple", "Banana"};
ArrayList<String> fruits = new ArrayList<>(Arrays.asList(elements));

Or using the List.of() helper added in Java 9:

List<String> elements = List.of("Apple", "Banana"); 
ArrayList<String> fruits = new ArrayList<>(elements);  

addAll() joins other collections:

ArrayList<String> moreFruits = new ArrayList<>();
moreFruits.add("Orange");

fruits.addAll(moreFruits); 

Great, let‘s now access our fruits next!

Retrieving Elements

Elements are GET using get(index) like arrays:

String first = fruits.get(0);  

System.out.println(first); // Banana

We can also iterate using the for-each style:

for(String fruit : fruits) {
  System.out.println(fruit);   
}

And don‘t forget iterators:

Iterator iter = fruits.iterator();
while(iter.hasNext()) {
  String fruit = (String) iter.next();
  System.out.println(fruit);
}

The iterator avoids concurrent modification exceptions when adding/removing elements during traversal.

Now you know how to easily access ArrayList elements!

Removing Elements

Time to DELETE elements using:

fruits.remove("Apple"); // by object

fruits.remove(0); // by index 

Check if an ArrayList contains an element:

boolean hasOrange = fruits.contains("Orange");

We can clear all elements with:

fruits.clear();  

And remove via iterator:

Iterator iter = fruits.iterator();
while(iter.hasNext()) {
  String fruit = (String) iter.next();  
  if(fruit.equals("Apple")) {
    iter.remove();
  }
}

You have the full gamut of removal operations now!

…Additional content continues