Explain Codes LogoExplain Codes Logo

How can I get a List from some class properties with Java 8 Stream?

java
streaming
java-8
best-practices
Anton ShumikhinbyAnton Shumikhin·Feb 10, 2025
TLDR

With Java 8 Streams, you can retrieve a list of properties from objects by mapping each object to its property. Get this done by invoking .stream() on your list, using .map() for property extraction, and consolidating the result with .collect(Collectors.toList()).

Example for a Person class with a getName method:

List<String> names = peopleList.stream() .map(Person::getName) .collect(Collectors.toList());

Key takeaway: Use .map() to transform objects to properties and .collect() to gather the result.

When to use map and when to use flatMap

Both map and flatMap are handy for manipulating streams, but serve unique needs. Select map to transform individual objects within a stream. flatMap is the hero when each object in your stream holds a collection and you desire a single, "flattened" stream of the nested objects.

Example with flatMap: Given each Person object has a list of friends and you need all friends' names in a list:

List<String> friendNames = peopleList.stream() .flatMap(p -> p.getFriends().stream()) .map(Person::getName) .collect(Collectors.toList());

Remember: flatMap is your best friend when dealing with nested collections.

Sharpening the complex property angle

When the properties you strive to extract are complex, lambda expressions come to the rescue offering the flexibility for more elaborate operations on the stream.

Example: Building a list of addresses but in lowercase:

List<String> addressList = peopleList.stream() .map(Person::getAddress) .map(String::toLowerCase) .collect(Collectors.toList());

Quick note: For clean, readable code, depend on method references for straightforward operations.

Playing safe with Optional properties

For properties that could potentially be null, the Optional type in Java 8 enables you to prevent NullPointerExceptions. Map through Optional safely by clubbing the filter and map methods.

Example: Picking out non-null nicknames:

List<String> nicknames = peopleList.stream() .map(Person::getNickname) // Optional<String> .filter(Optional::isPresent) // Isn't me, it's Java guarding you .map(Optional::get) // Unwrap or return, no questions asked .collect(Collectors.toList());

Point to note: It's wise to have a good practice handling Optional for more reliable Java code.

Not just a collector but a master collector

toList() isn't the only cracker in Java 8's pocket. There's a variety of collectors designed to aggregate elements in various formats like toSet or joining.

Example: You have multiple Person names and you desire one majestic string:

String names = peopleList.stream() .map(Person::getName) .collect(Collectors.joining(", ")); //At your service, your majesty

Note: Try on other collectors like toSet(). You'd love them, especially when you need a collection with unique elements.

How about exceptions in streams

When operations in a stream are suspected to throw exceptions, wrap them up safely with lambda expressions to maintain flow and readability.

Example: Reading files, but IOException might be an uninvited guest:

List<Path> paths = ...; List<String> lines = paths.stream() .flatMap(p -> { try { return Files.lines(p); // Ninja lines, swift silent, and IOError-proof } catch (IOException e) { throw new UncheckedIOException(e); //Oops! Had to, really sorry. } }) .collect(Collectors.toList());

Tip: Cleverly conceal checked exceptions as runtime exceptions where it makes sense.