【问题标题】:Android: Comparison method violates its general contractAndroid:比较方法违反其一般合同
【发布时间】:2015-08-31 09:51:03
【问题描述】:

我的应用程序在一些中国 Android 手机上出现了很多崩溃,并出现以下错误:比较方法违反其一般合同!

我读到这与 Collections.sort 调用有关。

我不太确定是不是因为我的自定义比较器。

这里是错误发生的地方:

            if (!airports.isEmpty()) {
            Collections.sort(airports, new DistanceToAirportComparator(location));
            return airports.get(0);
        }

比较器看起来像这样:

public class DistanceToAirportComparator implements Comparator<Airport> {

private final Location location;

public DistanceToAirportComparator(Location location) {
    this.location = location;
}

@Override
public int compare(Airport airport1, Airport airport2) {
    Location loc1 = getLocation(airport1);
    Location loc2 = getLocation(airport2);
    return Double.compare(location.distanceTo(loc1), location.distanceTo(loc2));
}

private Location getLocation(Airport airport){
    Location location = new Location(LocationManager.GPS_PROVIDER);
    location.setLatitude(airport.getLocation().getLatitude());
    location.setLongitude(airport.getLocation().getLongitude());
    return location;
}

所以我不太确定 Comparator 是否搞砸了,或者我是否需要以不同的方式调用 collections.sort

感谢任何帮助

【问题讨论】:

  • 您的排序调用是正确的。问题可能是您没有比较两个传递的对象,而是将两者与第三个对象进行比较。不过这只是猜测。
  • 如果您检查 Javadoc 的 ComparatorComparable,您将看到合同。单独测试您的比较器代码,看看是否有可能与它违约。
  • 你指的是 loc1 & loc2 吗?当您说第三个对象时,您的意思是返回 Double.compare?
  • 见@Derek Fung 的回答,他解释得更好。您的比较可能不稳定,因为它基于第三个组件,即比较器中保存的位置。
  • @F43nd1r 谢谢。只是不确定我需要在代码中更改什么来修复它。

标签: java android sorting comparator


【解决方案1】:

例外是这样说的:

比较器在某种意义上应该是正确的,如果 A > B 和 B > C,那么 A > C 一定为真。

所以,归根结底就是为什么会出现这样的问题。 我的猜测是,由于位置的性质,距离的计算是不稳定

我建议您预先计算距离并在结果上使用比较器。

编辑:

样本

private Airport sortByDistance(Location location) {
    List<Airport> airports;
    if (!airports.isEmpty()) {
        ArrayList<AirportWithDistance> distanceArrayList = new ArrayList<AirportWithDistance>(airports.size());
        for (Airport airport : airports) {
            distanceArrayList.add(new AirportWithDistance(airport, getDistance(airport, location)));
        }
        // replace start
        Collections.sort(distanceArrayList, new DistanceToAirportComparator());
        return airports.get(0).airport;
        // replace end
        //also seems u should replace the above to the line below instead, as you only wan min
        //return Collections.min(distanceArrayList, new DistanceToAirportComparator()).airport;
    }
    return null;
}


private static double getDistance(Airport airport, Location location){
    Location airportLocation = new Location(LocationManager.GPS_PROVIDER);
    airportLocation.setLatitude(airport.getLocation().getLatitude());
    airportLocation.setLongitude(airport.getLocation().getLongitude());
    return location.distanceTo(airportLocation);
}

public static class AirportWithDistance {
    Airport airport;
    double distance;

    AirportWithDistance(Airport airport, double distance) {
        this.airport = airport;
        this.distance = distance;
    }

}

public static class DistanceToAirportComparator implements Comparator<AirportWithDistance> {

    public DistanceToAirportComparator() {
    }

    @Override
    public int compare(AirportWithDistance airport1, AirportWithDistance airport2) {
        return Double.compare(airport1.distance, airport2.distance);
    }

}

【讨论】:

  • 当您说预计算时,您的意思是在 Compare 方法内部吗?就像我应该先获取最接近的并返回,而不是返回比较?
  • 有多种方法可以做到这一点,例如,您可以在类Airport中添加一个字段distance,并在排序前计算距离并设置字段值,然后对带有比较器的列表进行排序distance 字段。附言实际上这也会提高你的表现,因为减少了实际计算距离的次数。
  • 其实你应该先试一试,问问你实际遇到的问题,不过这次还是给你举个例子。另外,请检查您似乎不需要Collections.sort()Collections.min()
  • 部分代码修改,排序后需要.airport到达机场
  • 就 OO 而言,在机场内添加这是错误的,但作为一个肮脏的修复,它可以完成工作。如果你想让它在OO中更有意义,你可以看我的解决方案,不过这个要自己想办法。无论如何,只需检查解决方案即可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
相关资源
最近更新 更多