Question
I want to combine two List<String> objects into a new list in Java.
Is there a simpler approach than this?
List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
Requirements:
- Do not modify the original lists.
- Use JDK-only solutions.
- Do not use external libraries.
It would also be helpful to see:
- a one-line approach, if possible
- a version that works with JDK 1.3
Short Answer
By the end of this page, you will understand how to combine two lists into a new list in Java without changing the original lists. You will learn the standard JDK approach, compact one-liners, older pre-generics style for JDK 1.3, and how this pattern is used in real Java codebases.
Concept
Combining two lists means creating a new list that contains the elements of both source lists in order.
In Java, the most common way to do this is:
- create a new list
- copy elements from the first list
- copy elements from the second list
This matters because lists are mutable. If you simply reuse one of the original lists and call addAll, you will change that original list. When code must preserve existing data, you should create a new list first.
For example:
listOne.addAll(listTwo); // modifies listOne
That may be fine in some programs, but it breaks the requirement here because listOne is no longer unchanged.
The safe pattern is to copy, then append:
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
This creates a separate list object. The original lists still exist unchanged.
A key idea here is the difference between:
- copying references into a new list
- deep-copying the objects inside the list
When you join two List<String> values, a new list container is created, but the contained objects are still the same element references. With String, that is usually fine because strings are immutable.
Mental Model
Think of each list as a train of boxes.
listOneis one trainlistTwois another train- the new list is a third train
You are not changing either original train. You are building a new train and attaching copies of the box positions from the first train and then the second train.
So the operation is not:
- "stretch the first train"
It is:
- "build a new train and load it with the contents of both trains"
That mental model helps explain why this version is safe:
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
The new train starts with everything from listOne, then adds everything from listTwo. The original trains stay as they were.
Syntax and Examples
The standard JDK approach is short and clear.
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
How it works
new ArrayList<>(listOne)creates a new mutable list containing the elements oflistOneresult.addAll(listTwo)appends all elements fromlistTwo
Example
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> listOne = Arrays.asList("A", "B");
List<String> listTwo = Arrays.asList("C", "D");
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
System.out.println(result); // [A, B, C, D]
System.out.println(listOne); // [A, B]
System.out.println(listTwo); // [C, D]
}
}
Step by Step Execution
Consider this example:
List<String> listOne = Arrays.asList("red", "green");
List<String> listTwo = Arrays.asList("blue", "yellow");
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
Step-by-step
-
listOnecontains:redgreen
-
listTwocontains:blueyellow
-
new ArrayList<>(listOne)creates a new list:redgreen
-
result.addAll(listTwo)appends all elements fromlistTwo:red
Real World Use Cases
Here are common places where developers combine lists without modifying the originals.
Building API response data
You may fetch items from two sources and return them together.
List<Item> featured = itemService.getFeaturedItems();
List<Item> regular = itemService.getRegularItems();
List<Item> allItems = new ArrayList<>(featured);
allItems.addAll(regular);
Merging validation errors
A form may have field-level errors and business-rule errors.
List<String> allErrors = new ArrayList<>(fieldErrors);
allErrors.addAll(ruleErrors);
Combining search results
An application may search local cache first, then a database.
List<Result> combined = new ArrayList<>(cachedResults);
combined.addAll(databaseResults);
Preparing UI data
A screen may display pinned items first, followed by normal items.
List<Task> displayTasks = new ArrayList<>(pinnedTasks);
displayTasks.addAll(otherTasks);
Data processing pipelines
One stage may produce warnings, another stage may produce notices.
Real Codebase Usage
In real Java projects, developers usually prefer the simplest readable solution.
Common pattern: copy then append
List<User> result = new ArrayList<>(activeUsers);
result.addAll(pendingUsers);
This is popular because it is:
- easy to read
- efficient enough for most cases
- supported across Java versions
- explicit about not mutating the originals
Pattern: pre-sizing for performance
If both lists are large, developers sometimes pre-size the target list.
List<String> result = new ArrayList<>(listOne.size() + listTwo.size());
result.addAll(listOne);
result.addAll(listTwo);
This can reduce internal array resizing.
Pattern: guard against null
In real systems, lists may be null due to old APIs or incomplete validation.
List<String> result = new ArrayList<>();
if (listOne != null) {
result.addAll(listOne);
}
if (listTwo != null) {
result.addAll(listTwo);
}
Pattern: return immutable output
Sometimes developers build a combined list and then prevent later modification.
Common Mistakes
Mistake 1: modifying an original list by accident
Broken example:
listOne.addAll(listTwo);
Why it is a problem:
listOneis changed- this breaks the requirement to keep original lists unchanged
Fix:
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
Mistake 2: assuming Arrays.asList() returns a normal resizable list
Broken example:
List<String> listOne = Arrays.asList("A", "B");
listOne.add("C"); // throws UnsupportedOperationException
Why it is a problem:
Arrays.asList()returns a fixed-size list view
Fix:
List<String> listOne = new ArrayList<>(Arrays.asList("A", "B"));
listOne.add("C");
Mistake 3: using double-brace initialization casually
Comparisons
| Approach | Modifies originals? | JDK-only? | Java version | Notes |
|---|---|---|---|---|
new ArrayList<>(listOne); result.addAll(listTwo); | No | Yes | 5+ | Best general solution |
listOne.addAll(listTwo) | Yes | Yes | 1.2+ | Short, but changes listOne |
new ArrayList<>(size); addAll(...); addAll(...); | No | Yes | 5+ | Good when you want pre-sizing |
Stream.concat(...).collect(...) | No | Yes |
Cheat Sheet
// Standard Java 5+ solution
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
// Pre-sized version
List<String> result = new ArrayList<>(listOne.size() + listTwo.size());
result.addAll(listOne);
result.addAll(listTwo);
// Java 8+
List<String> result = Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toList());
// JDK 1.3 style
List result = new ArrayList(listOne);
result.addAll(listTwo);
Rules to remember
addAllappends all elements from one collection to another.listOne.addAll(listTwo)modifieslistOne.new ArrayList<>(listOne)creates a new mutable copy.- Copying a list does not deep-copy the objects inside it.
Arrays.asList(...)gives a fixed-size list, not a fully resizableArrayList.
FAQ
What is the simplest way to join two lists in Java?
Use a new ArrayList initialized with the first list, then call addAll with the second list.
List<String> result = new ArrayList<>(listOne);
result.addAll(listTwo);
Does addAll modify the original list?
Yes. It modifies the list you call it on. That is why you should call it on a new list if you want to preserve the originals.
Is there a one-liner to combine two lists in Java?
A stream-based one-liner is possible in Java 8+:
List<String> result = Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toList());
For simple cases, the two-line ArrayList approach is usually clearer.
How do I combine two lists in JDK 1.3?
Use the non-generic form:
List result = new ArrayList(listOne);
result.addAll(listTwo);
Is copying a list the same as cloning the objects inside it?
No. The new list contains the same element references. Only the list container is new.
Which is better: or streams?
Mini Project
Description
Build a small Java utility that combines two lists of usernames into a new list without changing the originals. This demonstrates safe list copying, addAll, and how to verify that source data remains unchanged after the merge.
Goal
Create a method that returns a new combined list from two input lists while preserving the original lists.
Requirements
- Create a method that accepts two
List<String>parameters. - Return a new list containing all elements from the first list followed by all elements from the second list.
- Do not modify either input list.
- Print the original lists and the merged list to verify the behavior.
Keep learning
Related questions
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Choosing a @NotNull Annotation in Java: Validation vs Static Analysis
Learn how Java @NotNull annotations differ, when to use each one, and how to choose between validation, IDE hints, and static analysis tools.
Convert a Java Stack Trace to a String
Learn how to convert a Java exception stack trace to a string using StringWriter and PrintWriter, with examples and common mistakes.