Java List Interface
Table of Contents + β
In the last lesson you learned about Java Iterator and Iterable. Now letβs look at the most common shape of collection in everyday Java: the List. It keeps your items in order, lets you store the same value more than once, and lets you reach any item by its position number.
π€ What problem does a List solve?
Picture a music playlist. Songs have an order. You might add the same song twice. And you want to say βplay the fourth song now.β That is a List.
Without it, nothing else fits:
- A plain array holds order, but its size is fixed and it has almost no helpers.
- A
Setwould refuse your duplicate song. - A
Mapwould force you to invent a key for every song.
A List is an ordered collection that allows duplicates and gives access by index. Order is remembered, repeats are allowed, and every item sits at a numbered position you can jump to.
π§© Three things that make a List a List
A List makes three promises:
- It keeps insertion order. The order you add is the order you get back.
- It allows duplicates. The same value can appear many times.
- It gives index access. Positions start at
0, so the first item is at index0.
This example shows all three. We add three names, repeat one, and reach into the middle by position.
import java.util.ArrayList;import java.util.List;
public class ListBasics { public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("Alex"); names.add("Riya"); names.add("Alex"); // duplicate allowed
System.out.println(names); // order kept System.out.println(names.get(1)); // index access System.out.println(names.size()); // how many items }}When you run this, the output is:
Output
[Alex, Riya, Alex]Alex3The order matches how we added items. Alex shows up twice. And get(1) gave the second item, because counting starts at zero.
π οΈ The methods beyond Collection
Every List is a Collection, so it has add, remove, size, contains, clear, and loop support. A List adds index-based methods on top, because it knows about positions. The ones worth learning by heart:
get(i)returns the item at indexi.set(i, e)replaces the item at indexiwitheand returns the old one.add(i, e)insertseat indexiand shifts the rest to the right.remove(i)removes the item at indexiand shifts the rest to the left.indexOf(e)returns the first index whereeis found, or-1if it is missing.lastIndexOf(e)returns the last index whereeis found, or-1.subList(from, to)returns a view of part of the list, fromfromup to but not includingto.listIterator()gives you a cursor that can move both forward and backward.
Letβs put the main ones to work. We start with three colors, then read, replace, insert, and remove by position.
import java.util.ArrayList;import java.util.List;
public class IndexMethods { public static void main(String[] args) { List<String> colors = new ArrayList<>(); colors.add("red"); colors.add("green"); colors.add("blue");
System.out.println(colors.get(0)); // read first colors.set(1, "lime"); // replace second colors.add(1, "yellow"); // insert at index 1 colors.remove(3); // remove by index
System.out.println(colors); }}Run it and you get:
Output
red[red, yellow, lime]What happened, step by step:
get(0)printedred, the first item.set(1, "lime")swappedgreenforlime, so the list became[red, lime, blue].add(1, "yellow")pushedyellowinto position one and slid the rest right, giving[red, yellow, lime, blue].remove(3)deleted the item at index three, which wasblue, leaving[red, yellow, lime].
Searching with indexOf and lastIndexOf
When a value appears more than once, indexOf finds the first match and lastIndexOf finds the last.
import java.util.List;import java.util.ArrayList;
public class FindPositions { public static void main(String[] args) { List<String> letters = new ArrayList<>(List.of("a", "b", "a", "c", "a"));
System.out.println(letters.indexOf("a")); // first match System.out.println(letters.lastIndexOf("a")); // last match System.out.println(letters.indexOf("z")); // not found }}Here is the output:
Output
04-1The first a is at index 0, the last at index 4. And z is missing, so we get -1. That -1 means βnot foundβ, so always check for it before trusting the result.
Taking a slice with subList
subList(from, to) hands you a window into the list. It starts at from and stops just before to.
import java.util.List;import java.util.ArrayList;
public class SliceList { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40, 50));
List<Integer> middle = nums.subList(1, 4); System.out.println(middle); }}The output is:
Output
[20, 30, 40]So subList(1, 4) gave indexes one, two, and three. Index four is left out because the end is not included. One word of care: a subList is a live view, not a copy. Changing it changes the original list too.
π Iterating a list
You will loop over lists constantly. There are three clean ways.
import java.util.List;
public class LoopList { public static void main(String[] args) { List<String> fruits = List.of("apple", "mango", "pear");
// 1. for-each: simplest, when you just need each item for (String fruit : fruits) { System.out.println(fruit); }
// 2. index loop: when you need the position number for (int i = 0; i < fruits.size(); i++) { System.out.println(i + " -> " + fruits.get(i)); }
// 3. forEach with a lambda: short and modern fruits.forEach(System.out::println); }}The first loop prints each fruit. The second pairs each item with its index. The third does the same as the first in one line. Use for-each by default, and the index loop only when you need the number i.
A listIterator is a fourth option for special cases. It can move backward as well as forward, and add or replace items while you walk. Reach for it when you want to scan in reverse.
ποΈ The main implementations
List is an interface, so it only describes behavior. To hold data you pick a class that implements it. Three exist, each with a different engine inside:
ArrayListis backed by a growable array. Fast for reading by index, and the one you will use almost every time.LinkedListis backed by a chain of linked nodes. Faster for adding or removing at the start.Vectoris an older, thread-safeArrayList. Rarely used in new code, because its locking slows it down.
The short rule: use ArrayList unless you have a clear reason not to. Each has its own lesson. They all behave like a List on the outside, with the same get, set, add, and remove.
π― Program to the List interface
When you declare a variable, use the interface type List on the left, and pick the class only on the right.
import java.util.List;import java.util.ArrayList;
public class ProgramToInterface { public static void main(String[] args) { // good: the type is the interface List<String> tasks = new ArrayList<>(); tasks.add("write report");
System.out.println(tasks); }}Why does this help? Your code depends on the idea of a list, not one exact class. Switch to a LinkedList later by changing a single word, and nothing else breaks. Methods that accept List<String> take any kind of list. This is programming to the interface, and it keeps your code flexible.
π Immutable lists with List.of
Sometimes you want a list that can never change, like a fixed set of weekdays. List.of builds an immutable list, one that is locked and cannot be modified.
import java.util.List;
public class ImmutableList { public static void main(String[] args) { List<String> days = List.of("Mon", "Tue", "Wed");
System.out.println(days); System.out.println(days.get(2)); }}The output is plain:
Output
[Mon, Tue, Wed]WedThis is perfect for values that should stay fixed, and it makes your intent clear. Any attempt to add or remove from it will fail, which we see in the mistakes below.
β οΈ Common Mistakes
Mixing up remove by index and remove by value
List has two remove methods. remove(int index) deletes by position, remove(Object o) deletes by value. With Integer values, a bare number is read as an index, not a value.
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
// β you wanted to remove the value 20, but this removes index 20// (and crashes here because index 20 does not exist)nums.remove(20);To remove by value, hand it a real object so Java picks the other method.
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
// β
removes the value 20, not the indexnums.remove(Integer.valueOf(20));System.out.println(nums); // [10, 30]Trying to change a List.of list
A list made with List.of is locked. Any add, set, or remove throws an error at runtime.
List<String> days = List.of("Mon", "Tue");
// β this throws UnsupportedOperationExceptiondays.add("Wed");If you need a list you can change, copy it into a fresh ArrayList first.
List<String> days = new ArrayList<>(List.of("Mon", "Tue"));
// β
this is a real, editable list nowdays.add("Wed");System.out.println(days); // [Mon, Tue, Wed]Reaching for an index that is not there
Indexes run from 0 up to size() - 1. Ask for anything outside that range and the program crashes with an out-of-bounds error.
List<String> items = new ArrayList<>(List.of("a", "b", "c"));
// β size is 3, so the last valid index is 2System.out.println(items.get(3)); // IndexOutOfBoundsExceptionCheck the size, or use a loop that stops at the right place.
List<String> items = new ArrayList<>(List.of("a", "b", "c"));
// β
the loop never goes past the last valid indexfor (int i = 0; i < items.size(); i++) { System.out.println(items.get(i));}β Best Practices
- Declare variables as
List, the interface, and pick the class only when you create the object. This keeps your code easy to change later. - Reach for
ArrayListby default. Switch toLinkedListorVectoronly when you have a measured reason. - Use the for-each loop unless you truly need the index number.
- When the data should never change, build it with
List.ofso the lock is enforced for you. - To remove a numeric value, wrap it with
Integer.valueOf(...)so you do not accidentally remove by index. - Treat
-1fromindexOfas βnot foundβ and check for it before using the result. - Remember that
subListis a live view, not a copy. Wrap it innew ArrayList<>(...)if you want an independent copy.
π What Youβve Learned
- A List is an ordered collection that allows duplicates and gives index access.
- The index-based methods
get,set,add(i, e),remove(i),indexOf,lastIndexOf,subList, andlistIteratorgo beyond what plainCollectionoffers. ArrayList,LinkedList, andVectorare the three implementations, withArrayListas your everyday choice.- Programming to the
Listinterface keeps your code flexible. List.ofbuilds an immutable list, and trying to change it throws an error.- Watch out for
remove(int)versusremove(Object), modifying a locked list, and stepping past the last index.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What three things make a List a List?
Why: A List keeps insertion order, allows duplicate values, and lets you reach items by their index position.
- 2
For a List of Integer, how do you remove the value 20 rather than index 20?
Why: A bare int calls remove(int index). Wrapping it with Integer.valueOf forces remove(Object) so it removes by value.
- 3
What happens if you call add() on a list made with List.of?
Why: List.of creates an immutable list, so any change throws UnsupportedOperationException at runtime.
- 4
Why declare a variable as List rather than ArrayList?
Why: Programming to the List interface means your code depends on the behavior, not one exact class, so swapping is easy.
π Whatβs Next?
You now know what a List promises and how to work with one through its interface. Next we go deep on the implementation you will use most: the resizable, array-backed list with all its handy methods.