2014-07-21 27 views
3

我试图将一个“老方法”循环转换为基于流的方法。循环需要一大组元素并返回落在给定半径内的子集。结果按距离排序,结果本身具有计算的距离(用于演示)。它的工作方式很好,我不需要需要 Java8-ify它。但我真的很想。 :-)如果只是为了能够在这个吸盘上去.parallel()。Java 8流:您可以捕获/重用在过滤器中计算的值吗?

美中不足的是我的...过滤器()使用的计算值(距离),然后我需要在随后的地图()步骤(构建“与距离”实例)。假定距离计算很昂贵。这里的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),但它是一个一步接近平行()美化版:

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中做到这一点的优雅方式:(a)避免重复计算,(b)不会产生垃圾垃圾?

我可以用forEach()调用来做到这一点,混合旧的&新的方法......所以我至少可以利用流,但“优化”钙/滤波器/添加以古老的方式。有一个很好,简单,优雅的解决方案。帮我看到了光明......

回答

0

您可以使用flatMap融合一个filtermap阶段,这可以让你保持在一个局部变量计算出的距离,直到你知道它是有用的创建一个新的对象。

在这里,我已经将flatmapper提取到一个辅助方法中,因为我更喜欢这种风格,但它肯定可能将它内联为语句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

我没有实际测试,它的作品,但我认为你可以计算的距离在map阶段,返回StationWithDistance或基于距离null,然后filternull秒。

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()); 
} 
相关问题