Как получить длину пути, используя потоки Java 8

Я имею List<Vector3D>, где Vector3D это координата. Я хочу найти сумму всех расстояний между Vector3D элементы списка. Я хочу найти его, используя потоки Java 8. Я пытаюсь использовать reduce но это не может помочь мне.

UPD:

Учебный класс Vector3D имеет метод double distance(Vector3D) найти расстояние между двумя позициями. Например, у меня есть список с (1,0,0) (2,0,0) (3,0,0). В результате я хочу найти длину этого пути. Это 3.

Если мы используем Java 7 или ниже, мы должны сделать:

public static double calcPathLength(List<Vector3D> path){
    double length = 0d;
    for (int i=0; i< path.size()-1; i++){
        length += path.get(i).distance(path.get(i+1));
    }
    return length;
}

2 ответа

Решение

Операция, которую вы выполняете, называется изменяемым сокращением.

Ответ Пшемо показывает, как вы можете реализовать такую ​​операцию ad-hoc, предоставив три необходимые функции. Однако, когда все три функции реализованы выделенным классом, может быть полезно реализовать эти функции внутри класса, реализующего Collector для более легкого повторного использования:

public class Distance implements Collector<Vector3D, Distance.Helper, Double> {

    public static final Distance COLLECTOR = new Distance();

    static final class Helper {
        private double sum = 0;
        private Vector3D first = null, previous = null;
    }
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
    public Supplier<Helper> supplier() {
        return Helper::new;
    }
    public BiConsumer<Helper, Vector3D> accumulator() {
        return (helper,vector3d)-> {
            if (helper.previous != null)
                helper.sum += vector3d.distance(helper.previous);
            else helper.first = vector3d;
            helper.previous = vector3d;
        };
    }
    public BinaryOperator<Helper> combiner() {
        return (h1,h2)-> {
            h2.sum += h1.sum;
            if(h1.previous!=null && h2.first!=null) {
                h2.sum += h1.previous.distance(h2.first);
                h2.first=h1.first;
            }
            return h2;
        };
    }
    public Function<Helper, Double> finisher() {
        return helper -> helper.sum;
    }
}

Вы узнаете три функции из специальной версии. Новый это четвертая функция, finisher который позволяет указать, как конечный результат может быть извлечен из изменяемого контейнера, поэтому нам не нужно getSum() вызов.

Вариант использования упрощается до:

List<Vector3D> list;
//…
double distance=list.stream().collect(Distance.COLLECTOR);

Одним из вариантов будет создание некоторого вспомогательного класса, который запомнит ранее использованный вектор и на его основе вычислит разницу между ним и текущим вектором. Этот класс может выглядеть как

class DistanceHelper {
    private double sum = 0;
    private Vector3D first = null;
    private Vector3D last = null;

    public void add(Vector3D vector3d) {
        if (first == null)
            first = vector3d;
        if (last != null)
            sum += vector3d.distance(last);
        last = vector3d;
    }

    public void combine(DistanceHelper otherHelper) {
        //add distance of path from current thread with distance of path
        //from other thread
        sum += otherHelper.sum;
        //also add distance between paths handled by separate threads like
        // when path of Thread1 is A->B and Thread2 is C->D then we need to 
        // include path from `B` to `C`
        if (this.last!=null && otherHelper.first!=null)
            sum += this.last.distance(otherHelper.first);
        this.last = otherHelper.last;
    }

    public double getSum() {
        return sum;
    }
}

и вы можете использовать его, например, с combine вместо reduce лайк

double sum = list
        .stream()//or parallelStream()
        .collect(DistanceHelper::new, DistanceHelper::add,
                DistanceHelper::combine).getSum();
Другие вопросы по тегам