Java stream on specific fields in a custom class object
I have an ArrayList
of Train
objects.
Each Train
has three fields: source
, destination
, cost
.
I want to get all the place names, i.e. all distinct sources destinations.
I am using the below code, but as it can be observed, I'm using two streams to retrieve the data.
List<String> destinations = list.stream()
.map(x -> x.getDestination())
.distinct()
.collect(Collectors.toList());
List<String> sources = List.stream()
.map(x -> x.getSource())
.distinct()
.collect(Collectors.toList());
I was wondering how I could accomplish the same thing in a single stream? Can it be done using flatMap
, or there's another way to achieve this?
List<String> allPlaces = ?
Also, is this possible to use Train
class without getters?
CodePudding user response:
You had the right idea with flatMap
- you can map a train to a stream that contains the source and destination, and then flatMap
it to you "main" stream:
List<String> allPlaces =
trains.stream()
.flatMap(t -> Stream.of(t.getSource(), t.getDestination()))
.distinct()
.collect(Collectors.toList());
CodePudding user response:
In this case, we can utilize Java 16 method mapMulti()
, which is functionally similar to flatMap()
. It's meant for transforming a single stream element into a group of elements.
Here's how implementation might look like:
List<String> places = trains.stream()
.<String>mapMulti((train, consumer) -> {
consumer.accept(train.getSource());
consumer.accept(train.getDestination());
})
.distinct()
.toList();
Contrary to flatMap()
it doesn't consume a stream, but operates via Consumer
. mapMulti()
a recommended alternative to flatMap()
for situations when a new stream flatMap()
requires would contain only a few elements (like in this case when we have only two elements: source and destination).
A quote from the API Note:
This method is preferable to
flatMap
in the following circumstances:
- When replacing each stream element with a small (possibly zero) number of elements. Using this method avoids the overhead of creating a new Stream instance for every group of result elements, as required by
flatMap
. Also is this possible without the getters methods of classTrain
?
Sure, you can. But it's not a recommended practice to access instance fields directly. In Java we're using access modifier to hide and protect member-variables within the class, that's one of the aspects of Encapsulation.