Java 8流:您可以捕获/重用过滤器中计算的值吗?

时间:2014-07-21 20:20:46

标签: java filter parallel-processing java-8 garbage

我正在尝试将“旧方式”循环转换为基于流的方法。循环采用一大组元素并返回属于给定半径的子集。结果按距离排序,结果本身具有方便的计算距离(用于演示)。它以旧的方式运行良好,我不需要到Java8-ify它。但我真的很想。 :-)如果只能在这个吸盘上去.parallel()。

捕获是...我的filter()使用计算值(距离),然后我需要在后续map()步骤中(构造“with distance”实例)。假设距离计算很昂贵。这是Java 7方式...向下滚动以查看getNearestStations()方法:

public interface Coordinate {
    double distanceTo(Coordinate other);
}

public class Station {
    private final String name;
    private final Coordinate coordinate;

    public Station(String name, Coordinate coordinate) {
        this.name = name;
        this.coordinate = coordinate;
    }

    public String getName() {
        return name;
    }

    public Coordinate getCoordinate() {
        return coordinate;
    }
}

public class StationWithDistance extends Station implements Comparable<StationWithDistance> {
    private final double distance;

    public StationWithDistance(Station station, double distance) {
        super(station.getName(), station.getCoordinate());
        this.distance = distance;
    }

    public double getDistance() {
        return distance;
    }

    public int compareTo(StationWithDistance s2) {
        return Double.compare(this.distance, s2.distance);
    }
}

// Assume this contains many entries
private final List<Station> allStations = new ArrayList<>();

public List<StationWithDistance> getNearbyStations(Coordinate origin, double radius) {
    List<StationWithDistance> nearbyStations = new ArrayList<>();
    for (Station station : allStations) {
        double distance = origin.distanceTo(station.getCoordinate());
        if (distance <= radius) {
            nearbyStations.add(new StationWithDistance(station, distance));
        }
    }
    Collections.sort(nearbyStations);
    return nearbyStations;
}

现在......这是一种基于愚蠢/蛮力流的方法。请注意,它执行两次距离计算(stoopid),但它更接近并行()ized:

public List<StationWithDistance> getNearbyStationsNewWay(Coordinate origin, double radius) {
    return allStations.stream()
        .parallel()
        .filter(s -> origin.distanceTo(s.getCoordinate()) <= radius)
        .map(s -> new StationWithDistance(s, origin.distanceTo(s.getCoordinate())))
        .sorted()
        .collect(Collectors.toList());
}

试图找出一个更好的方式(tm),这是我到目前为止所提出的所有内容,以避免重复计算:

public List<StationWithDistance> getNearbyStationsNewWay(Coordinate origin, double radius) {
    return allStations.stream()
        .parallel()
        .map(s -> new StationWithDistance(s, origin.distanceTo(s.getCoordinate())))
        .filter(s -> s.getDistance() <= radius)
        .sorted()
        .collect(Collectors.toList());
}

...但是会产生垃圾 - 大多数创建的StationWithDistance实例都会被过滤掉。

我错过了什么?在Java 8中是否有一种优雅的方法可以避免重复计算,(b)不会产生不必要的垃圾?

我可以通过forEach()调用,混合旧的&amp;新的方法......所以我至少可以利用流,但以老派的方式“优化”计算/过滤/添加。这将是一个很好,简单,优雅的解决方案。帮我看看光......

2 个答案:

答案 0 :(得分:1)

您可以使用flatMap融合filtermap阶段,这样您就可以将计算出的距离保存在局部变量中,直到您知道创建新对象有用为止

在这里,我已经将flatmapper提取为一个helper方法,因为我更喜欢这种风格,但它当然可能将它内联为语句lambda(甚至是使用? :三元运算符的表达式lambda)

Stream<StationWithDistance> nearbyStation(Station s, Coordinate origin, double radius) {
    double distance = origin.distanceTo(s.getCoordinate());
    if (distance <= radius) {
        return Stream.of(new StationWithDistance(s, distance));
    } else {
        return Stream.empty();
    }
}

public List<StationWithDistance> getNearbyStationsNewerWay(Coordinate origin, double radius) {
    return allStations.stream()
        .parallel()
        .flatMap(s -> nearbyStation(s, origin, radius))
        .sorted()
        .collect(Collectors.toList());
}

答案 1 :(得分:1)

我实际上没有测试它是否有效,但我认为您可以根据距离计算map阶段的距离,返回StationWithDistancenull,以及然后filter null s。{/ p>

public List<StationWithDistance> getNearbyStationsNewWay(Coordinate origin, double radius) {
    return allStations.stream()
        .parallel()
        .map(s -> {
                     double dist = origin.distanceTo(s.getCoordinate());
                     return (dist <= radius)?(new StationWithDistance(s, dist)):null;
                  })
        .filter(s -> s != null)
        .sorted()
        .collect(Collectors.toList());
}