Reputation: 475
I have a List of custom entities from which i want to valuate a field using a method,
and that i want to filter next. I'm fairly new to java streams,
and i don't know if it's better to use map and filters, or to use a more traditional forEach
.
Here is my first attempt:
public List<Restaurant> findRestaurantsWithin(Double latitude, Double longitude, Integer radius) {
log.info("Searching restaurants {} km to point {} lat., {} long.", radius, latitude, longitude);
List<Restaurant> restaurants = this.restaurantRepository.findAll();
restaurants.forEach(restaurant ->
{
if (restaurant.getLatitude() != null) {
restaurant.setDistance(
this.getDistance(Double.parseDouble(restaurant.getLatitude()),
Double.parseDouble(restaurant.getLongitude()), latitude, longitude)
);
}
}
);
return restaurants.stream()
.filter(restaurant -> restaurant.getDistance() <= radius)
.sorted(Comparator.comparing(Restaurant::getDistance))
.skip(size * page - 1)
.limit(size)
.collect(Collectors.toList());
And here is the second :
return this.restaurantRepository.findAll().stream()
.filter(restaurant ->
{
if (restaurant.getLatitude() != null) {
Double distance = this.getDistance(Double.parseDouble(restaurant.getLatitude()), Double.parseDouble(restaurant.getLongitude()), latitude, longitude);
if (distance <= radius) {
restaurant.setDistance(distance);
return true;
}
}
return false;
})
.sorted(Comparator.comparing(Restaurant::getDistance))
.skip(size * page - 1)
.limit(size)
.collect(Collectors.toList());
On the second one i should probably use a .map
first, but i'm not sure that there will be a difference in performance. Is there a better practice or more elegant way to get that ?
Thanks !
Upvotes: 0
Views: 743
Reputation: 44200
I'd say that Restaunt::setDistance
is the big code smell here. Each restaurant is capable of holding property which says how far away it is from some other arbitrary point. We don't know where or what that point is, or when it was set. It's not a true property of a restaurant, it's just a hack.
Side effects and functional programming in general do not really mix well either, which is another reason both of your examples might seem clunky to you.
Here's how I'd do it, with a general purpose Pair
class (there are many but the exact implementation shouldn't matter. Java FX was removed in I think Java 11, but if you're using a version of Java before that, javafx.util.Pair
requires no dependencies)
return restaurantRepository.findAll().stream()
.filter(restaurant -> restaurant.getLatitude() != null)
.map(restaurant -> new Pair<>(restaurant, this.getDistance(/*blah blah*/)))
.filter(resAndDistance -> resAndDistance.getValue() <= radius)
.sorted(Comparator.comparing(Pair::getValue))
.skip(size * page - 1)
.limit(size)
.collect(Collectors.toList());
Upvotes: 3