As an experienced Java developer, you’ve likely encountered situations where you need to convert a Set to a List. Although Sets and Lists share similarities, each collection type has distinct behaviors that suit certain use cases better. Converting between them is a handy tool for any Java programmer.
In this comprehensive tutorial, we’ll explore when and why converting a Set to a List is useful through concrete examples. You’ll learn:
- The key differences between
SetandListin Java - When to choose one over the other
- Multiple techniques for converting
SettoList - How order, duplicates, and performance are affected
- Best practices for conversion based on your needs
Follow along and you‘ll gain the knowledge to confidently convert between Set and List in your own code!
Sets vs. Lists Refresher
Before jumping into conversion techniques, let‘s recap the core differences between Set and List in Java.
A Set is an unordered collection that cannot contain duplicate elements. List is an ordered sequence that can contain duplicates.
Some key Set behaviors:
- Unique elements only
- Fast membership checks with
contains() - Common implementations:
HashSet,TreeSet,LinkedHashSet
Set<String> fruitSet = new HashSet<>();
fruitSet.add("apple");
fruitSet.add("banana");
fruitSet.add("orange");
// Order arbitrary, no duplicates
System.out.println(fruitSet);
In contrast, Lists maintain element ordering and allow duplicates:
- Elements in insertion order
- Index-based access with
get(index) - Common implementations:
ArrayList,LinkedList
List<String> fruitList = new ArrayList();
fruitList.add("apple");
fruitList.add("banana");
fruitList.add("apple");
// Prints in insertion order
System.out.println(fruitList);
This high-level comparison shows when Sets and Lists shine. Now let‘s see why converting between them is useful.
Why Convert Set to List in Java?
Some situations where converting a Set to List comes in handy:
Maintaining Order
Sets do not preserve insertion order (except LinkedHashSet). By converting to a List, you can maintain the element order after processing or manipulating a Set:
Set<String> nameSet = getNameSetFromDatabase(); // unordered
List<String> orderedNames = new ArrayList<>(nameSet); // preserve order
Allowing Duplicates
Sets do not permit duplicate elements. Converting to a List enables working with duplicates:
Set<Product> productSet = fetchUniqueProducts();
// Want duplicates for business reporting
List<Product> productList = new ArrayList<>(productSet);
Indexed Access
Lists allow accessing elements by index position, while Sets only permit iteration:
Set<Integer> idSet = loadSetOfCustomerIds();
List<Integer> idList = new ArrayList<>(idSet);
// Access by index
int customerId = idList.get(0);
Interoperating with List-based APIs
Many libraries expect List input, so converting Set allows interfacing with them:
Set<String> keywords = getKeywordsFromAnalytics();
// Pass List to 3rd party API
List<String> keywordsList = new ArrayList<>(keywords);
tagCloudAPI.buildCloud(keywordsList);
There are many other examples like sorting a list, binding to GUI components, etc. Converting to List provides flexibility.
Now let‘s dig into the techniques!
Technique 1: List Constructor
The simplest way to convert a Set to List is passing the Set into the List constructor:
Set<String> fruitSet = new HashSet<>();
fruitSet.add("apple");
fruitSet.add("orange");
fruitSet.add("banana");
// Convert to List via constructor
List<String> fruitList = new ArrayList<>(fruitSet);
System.out.println(fruitList);
// [apple, orange, banana] - order varies
This creates a new ArrayList initialized with the Set elements. Order will be lost since Sets are inherently unordered.
The constructor approach works for any Set implementation like HashSet or TreeSet. For example:
SortedSet<Integer> sortedSet = new TreeSet<>();
// add sorted int values
List<Integer> sortedIntList = new ArrayList<>(sortedSet);
// List reflects Set‘s ordering
Pros
- Simple, compact syntax
- Works with any
Setimplementation
Cons
- Order not preserved
Preserving Insertion Order
To retain order, first create an empty ordered List like ArrayList or LinkedList, then use List.addAll(Set):
Set<String> fruitSet = new LinkedHashSet<>(); // preserves insertion order
fruitSet.add("apple");
fruitSet.add("orange");
fruitSet.add("banana");
List<String> fruitList = new ArrayList<>();
fruitList.addAll(fruitSet); // add Set to empty List
// Prints in insertion order
System.out.println(fruitList);
Since LinkedHashSet maintains insertion order, calling addAll() copies the Set into the List in order.
For custom Set types, implement SortedSet or a custom order-preserving collection.
Technique 2: addAll() Method
In addition to the constructor, we can use the List.addAll() method directly:
Set<Integer> numberSet = new HashSet<>();
numberSet.add(42);
numberSet.add(5);
numberSet.add(10);
List<Integer> numberList = new ArrayList<>();
// Add all Set elements to empty List
numberList.addAll(numberSet);
// Number order not preserved
System.out.println(numberList);
This separately initializes an empty List then appends the Set elements to it.
Just like before, use an ordered Set like LinkedHashSet to maintain insertion order after converting.
Pros
- Can preserve order
- Appends Set elements in a single call
Cons
- Requires separate List initialization
Technique 3: List.copyOf()
The static List.copyOf() method returns an immutable List containing the elements of a given collection:
Set<Integer> numberSet = Set.of(10, 5, 2); // immutable set
// Create immutable List copy
List<Integer> numberList = List.copyOf(numberSet);
System.out.println(numberList);
// [10, 5, 2] - order not guaranteed
The key distinction is the returned List cannot be modified once created.
To preserve order, supply an ordered Set:
Set<String> orderedSet = new LinkedHashSet<>();
orderedSet.add("foo");
orderedSet.add("bar");
// Get immutable, ordered List copy
List<String> immutableList = List.copyOf(orderedSet);
Pros
- Returns a read-only List
- Concise one-liner
Cons
- Resulting List is immutable
Technique 4: Streams
We can leverage Java‘s stream API to convert as well:
Set<String> nameSet = Set.of("John", "Sarah", "Mike");
List<String> nameList = nameSet.stream()
.collect(Collectors.toList());
System.out.println(nameList);
// No guaranteed order
The Set is streamed into a new ArrayList via collect(). Order is arbitrary since streams do not preserve order like our previous examples.
Pros
- Integrates with other stream operations
- Succinct syntax
Cons
- Order not maintained
Technique 5: Manual Conversion
For the most control, we can manually iterate the Set and add elements to a List one by one:
Set<MyClass> mySet = new HashSet<>();
// populate set
List<MyClass> myList = new ArrayList<>();
// Add each Set element to the List
for (MyClass element : mySet) {
myList.add(element);
}
// myList contains the Set elements
The manual approach allows customizing ordering and behavior during conversion.
We can sort elements, filter, or modify them as we add to the List:
Set<Product> productSet = loadProducts();
List<Product> saleProducts = new ArrayList<>();
for (Product p : productSet) {
if (p.isOnSale()) {
saleProducts.add(p);
}
}
// saleProducts contains just on-sale products!
Pros
- Fine-grained control over conversion logic
- Customize ordering, filtering, element mutation
Cons
- More verbose than other approaches
Comparing Set and List Performance
In addition to behavior differences, Set and List have performance characteristics to consider when converting between them.
Insertion and Deletion
Inserting and deleting elements is generally faster for Lists than Sets:
| Operation | ArrayList | HashSet |
|---|---|---|
| Insertion | O(1) | O(1) |
| Deletion | O(n) | O(1) |
ArrayList insertion is constant time since it simply appends to an array. HashSet has near constant time insertion via hashing.
However, deleting is slower for ArrayList since subsequent elements must be shifted over. HashSet remove is just a map operation.
Memory Overhead
Sets generally have lower memory overhead than Lists since they do not store ordering information:
| Collection | Memory Complexity |
|---|---|
ArrayList |
O(n) |
LinkedList |
O(n) |
HashSet |
O(n) |
TreeSet |
O(n) |
Though complexity is similar, constant factors make List implementations require more memory per element.
Lookup Time
Checking membership with contains() is much faster for HashSet than ArrayList, since HashSet uses hash lookup:
| Collection | Contains Lookup |
|---|---|
ArrayList |
O(n) |
HashSet |
O(1) |
So HashSet shines for fast membership checks.
Benchmarking your own use cases is recommended, but in general Sets have faster lookups while Lists perform better for insertions and deletions.
Converting Custom Types
So far we‘ve looked at converting built-in types like String and Integer between Set and List. Let‘s look at examples with custom classes:
// Custom class
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
// Getters, equals(), hashCode() omitted for brevity
}
Set<Person> personSet = createPersonSet();
// Convert to List
List<Person> personList = new ArrayList<>(personSet);
For custom types, be sure to implement equals() and hashCode() correctly for proper Set behavior.
The techniques we have covered work for any object types, enabling conversion between Set and List for your own classes.
Key Takeaways and Best Practices
Let‘s recap key learnings and best practices:
- Use
Listconstructor oraddAll()to retainSetinsertion order - Prefer immutable
copyOf()if defending against changes - Manual conversion allows maximum control over ordering and filtering
Some other tips:
- Use
LinkedHashSetif insertion order must be maintained - Leave null values out of Sets to avoid
NullPointerException - Exclude duplicates from Sets with
Set.of()for immutability - Employ
SortedSetimplementations to sort Lists after converting
Think about your specific requirements and performance needs when choosing an approach. This guide has equipped you to easily convert between Set and List in Java.
Happy coding!


