Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Given a Map<K, V> and a List<K>, start with the key list and look up each key. The direct result is a List<V>:
List<V> values = keys.stream()
.map(map::get)
.toList();
This follows the order of keys and keeps repeated keys. It also puts null in the result for a key with no mapping. The details that matter most are what to do about missing keys and whether the result should be mutable. If you need List<T> rather than List<V>, add a conversion from each value to T.
Why the lookup starts with the key list
The operation is: for each key in keys, look up its value in map and append that value. Starting the stream from keys makes the requested list—not the map’s iteration order—determine the result order.
Map<Integer, String> names = Map.of(
1, "Ada",
2, "Grace"
);
List<Integer> ids = List.of(2, 1);
List<String> result = ids.stream()
.map(names::get)
.toList();
// ["Grace", "Ada"]
Repeated keys are repeated lookups, so they produce repeated values. For example, keys ["b", "a", "b"] produce the value for "b", then "a", then "b" again. Use .distinct() before the lookup only if you deliberately want to keep the first occurrence of each key and discard later ones.
By contrast, map.values() or a stream over map.entrySet() returns values in the map’s encounter order and ignores the supplied key list. A HashMap does not promise a stable encounter order; LinkedHashMap and TreeMap have defined ordering behavior of their own. For the order of the caller’s keys, always iterate over keys. See the Java Map API.
Choose what happens when a key is missing
Map.get(key) returns null when the map has no mapping for that key. If the map permits null values, it can also return null when the key is present and explicitly maps to null. Choose the behavior that fits your data rather than assuming every null means the same thing.
Keep a position for every requested key
List<V> values = keys.stream()
.map(map::get)
.toList();
This preserves the list’s length and positions, but a missing key appears as null. A stored null value is indistinguishable from a missing key in this result.
Skip keys that are absent
List<V> values = keys.stream()
.filter(map::containsKey)
.map(map::get)
.toList();
This keeps values only for keys that have a mapping and preserves the relative order of those keys. If a present key maps to null, that null is kept. The distinction between get() and containsKey() is documented in the Java Map API.
Rank #2
Substitute a fallback
V fallback = ...;
List<V> values = keys.stream()
.map(key -> map.getOrDefault(key, fallback))
.toList();
getOrDefault() uses the fallback if the key has no mapping. A key explicitly mapped to null still has a mapping, so its result can remain null. If you want to replace both absent keys and null values, use a null check instead:
List<V> values = keys.stream()
.map(key -> {
V value = map.get(key);
return value != null ? value : fallback;
})
.toList();
Use containsKey() when null is a valid stored value and must be treated differently from absence.
Fail when a key is absent
A loop makes validation and error reporting straightforward:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
List<V> values = new ArrayList<>(keys.size());
for (K key : keys) {
if (!map.containsKey(key)) {
throw new IllegalArgumentException("Missing key: " + key);
}
values.add(map.get(key));
}
This accepts a present key whose value is null. If that is not valid for your application, check for null separately and report that condition explicitly.
Choose the result’s mutability and Java version
Stream.toList() is available from Java 16 and returns an unmodifiable list. It preserves the stream’s encounter order, but does not promise a particular implementation type. Attempts to modify it, such as calling add(), throw UnsupportedOperationException. See the Java Stream.toList() documentation.
For Java 8 through 15, a common option is:
List<V> values = keys.stream()
.map(map::get)
.collect(Collectors.toList());
Do not rely on Collectors.toList() to return a particular list type or to guarantee mutability. If you need a mutable ArrayList, request one explicitly:
List<V> values = keys.stream()
.map(map::get)
.collect(Collectors.toCollection(ArrayList::new));
Both collector forms are available from Java 8. The collector contracts are described in the Java Collectors API.
Recommended Free Tools
If you want an unmodifiable list on Java 10 or later, Collectors.toUnmodifiableList() is another option. Unlike Stream.toList(), it rejects null elements. List.copyOf() also creates an unmodifiable list and rejects null elements, so it is not suitable when a missing lookup is being represented as null. See the Java List.copyOf() documentation.
Rank #4
When the result really needs to be List<T>
A lookup in a Map<K, V> produces a V. To produce a T, apply a conversion after the lookup. This example omits absent keys:
List<T> result = keys.stream()
.filter(map::containsKey)
.map(map::get)
.map(converter)
.toList();
For example, if userMap maps IDs to User objects and UserDto.from converts a user to a DTO:
List<UserDto> users = userIds.stream()
.filter(userMap::containsKey)
.map(userMap::get)
.map(UserDto::from)
.toList();
The filter is a policy choice: remove it if missing keys should reach the converter as null or be handled another way. A reusable helper can accept the conversion function:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchstatic <K, V, T> List<T> selectAndMap(
Map<K, V> map,
List<K> keys,
Function<? super V, ? extends T> converter) {
return keys.stream()
.filter(map::containsKey)
.map(map::get)
.map(converter)
.toList();
}
The wildcard bounds allow a converter that accepts a supertype of V and returns a subtype of T. Add imports for java.util.Map, java.util.List, and java.util.function.Function. For Java 8–15, replace .toList() with .collect(Collectors.toList()).
Best Value
When a loop is the better fit
Streams are concise for a simple lookup, but a loop is often clearer when you need a mutable result, detailed validation, logging, multiple conditions, or a custom failure policy:
List<V> values = new ArrayList<>(keys.size());
for (K key : keys) {
if (!map.containsKey(key)) {
continue; // Or substitute a value or throw an exception.
}
values.add(map.get(key));
}
This version skips absent keys but retains a null value for a present key if the map permits one. If every requested key should have a result, omit the continue policy and decide explicitly whether absence should mean null, a fallback, or an exception.
Performance and concurrent changes
The operation performs one lookup per requested key. With a typical hash-based map, lookup is expected to be constant time, so looking up n keys is generally expected to take about O(n). A tree-based map typically takes O(log m) per lookup for a map of size m, giving about O(n log m). These are implementation-dependent expectations, not guarantees of the Map interface. The output list itself uses space proportional to the number of values retained.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →If another thread can change the map while lookups run, the result may reflect different map states across the list. In particular, filtering with containsKey() and then calling get() does not make those two calls an atomic lookup. If you need a consistent snapshot or atomic behavior, establish it using the synchronization or data-structure guarantees appropriate to your application.
Quick choice guide
| Requirement | Use |
|---|---|
| Follow key-list order | Stream or loop over keys |
| Keep duplicate requests | Do not call distinct() |
| Represent missing keys as null | .map(map::get) |
| Skip absent keys | .filter(map::containsKey).map(map::get) |
| Use a fallback for absent keys | .map(k -> map.getOrDefault(k, fallback)) |
| Require a mutable ArrayList | Collectors.toCollection(ArrayList::new) |
| Convert values from V to T | Add a second .map(converter) |
| Need detailed validation | Use a conventional loop |
If keys is null, calling keys.stream() throws NullPointerException. Decide at the API boundary whether null should be rejected or explicitly treated as an empty list; do not silently choose the latter if it would hide a caller error. A null map likewise normally indicates invalid input and should not be silently ignored.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

