Tengo una lista de entidades personalizadas de las que quiero valorar un campo usando un método, y que quiero filtrar a continuación. Soy bastante nuevo en las transmisiones de Java y no sé si es mejor usar mapas y filtros, o usar un forEach más tradicional. Aqui esta mi primer intento:

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());

Y aquí está el segundo:

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());

En el segundo, probablemente debería usar un .map primero, pero no estoy seguro de que haya una diferencia en el rendimiento. ¿Existe una mejor práctica o una forma más elegante de conseguirlo? Gracias !

0
Alain Duguine 20 jul. 2020 a las 14:24

1 respuesta

La mejor respuesta

Yo diría que Restaunt::setDistance es el gran olor a código aquí. Cada restaurante es capaz de tener una propiedad que dice qué tan lejos está de algún otro punto arbitrario. No sabemos dónde ni cuál es ese punto, ni cuándo se estableció. No es una verdadera propiedad de un restaurante, es solo un truco.

Los efectos secundarios y la programación funcional en general tampoco se combinan bien, lo cual es otra razón por la que ambos ejemplos pueden parecerle torpes.

Así es como lo haría, con una clase Pair de propósito general (hay muchas, pero la implementación exacta no debería importar. Java FX se eliminó en, creo, Java 11, pero si está utilizando una versión de Java antes de eso, javafx.util.Pair no requiere dependencias)

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());
3
Michael 20 jul. 2020 a las 11:42