Java
Programming
Code Optimization
Software Development
Data Structures

Collections.emptyList() vs. new instance

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Collections.emptyList() and new ArrayList<>() both represent an empty list, but they communicate different contracts. The first says “here is an empty immutable list,” while the second says “here is a new mutable list you can change.”

Collections.emptyList() Returns a Shared Immutable Empty List

Collections.emptyList() returns an empty list instance that cannot be modified.

java
1import java.util.Collections;
2import java.util.List;
3
4List<String> values = Collections.emptyList();
5System.out.println(values.isEmpty());

Because the returned object is immutable and shared, it avoids creating a new list object every time. That makes it a strong default when a method simply needs to return “no results.”

java
public List<String> findNames() {
    return Collections.emptyList();
}

This is especially useful for API methods that promise the caller a list but have nothing to return.

new ArrayList<>() Creates a Fresh Mutable Object

A new list instance is appropriate when the caller or the method itself needs mutability.

java
1import java.util.ArrayList;
2import java.util.List;
3
4List<String> values = new ArrayList<>();
5values.add("alpha");
6System.out.println(values);

Each call allocates a new object, and callers can append, remove, or replace entries. That is not a downside when mutation is part of the design; it is simply a different contract.

Choose Based on Mutability, Not Habit

The most important question is not micro-performance. It is whether the returned list should be modifiable.

If the answer is no, returning Collections.emptyList() is clearer than returning a mutable list that just happens to be empty at the moment.

If the answer is yes, return a real mutable list.

java
1public List<String> buildNames(boolean includeDefaults) {
2    List<String> result = new ArrayList<>();
3    if (includeDefaults) {
4        result.add("admin");
5    }
6    return result;
7}

That method clearly intends to build up a list over time, so a new instance is the correct tool.

Returning Empty Collections Is Better Than Returning null

A common API question sits behind this comparison: should a method return null when there is no data? In most Java APIs, the better answer is no.

java
public List<String> loadUsers() {
    return Collections.emptyList();
}

Returning an empty list lets callers iterate safely without extra null checks.

java
for (String user : loadUsers()) {
    System.out.println(user);
}

That is a cleaner contract than forcing every caller to guard against null before basic list operations.

Immutability Protects Callers and Implementations

Returning an immutable empty list can prevent accidental coupling between the caller and your implementation.

java
List<String> values = Collections.emptyList();
values.add("oops");

That throws UnsupportedOperationException, which is a useful signal that the API never intended the list to be modified.

By contrast, returning a new mutable list invites callers to treat the result as their own working collection. That may be exactly what you want, but the choice should be explicit.

Minor Performance Differences Matter Less Than API Meaning

Yes, Collections.emptyList() avoids repeated allocation of empty list objects. In very hot code paths that can be a small win, but most of the time the bigger benefit is semantic clarity.

A shared immutable empty list communicates intent better than a fresh mutable list. In Java, that kind of contract clarity tends to matter more than saving one tiny allocation in isolation.

Common Pitfalls

  • Returning new ArrayList<>() by habit when the API should really promise immutability.
  • Returning Collections.emptyList() and then expecting the caller to add items to it.
  • Treating the decision as purely a performance issue instead of a contract decision.
  • Forgetting that immutable and mutable empties behave differently under modification attempts.
  • Mixing both styles arbitrarily across the same API surface and making caller expectations inconsistent.

Summary

  • 'Collections.emptyList() returns a shared immutable empty list.'
  • 'new ArrayList<>() creates a fresh mutable empty list.'
  • Choose based on whether mutation is part of the contract.
  • Returning an empty list is usually better than returning null.
  • The semantic difference matters more than the tiny allocation difference.

Course illustration
Course illustration

All Rights Reserved.