为地理坐标类实现-hash和-isEqual

时间:2012-01-01 04:07:07

标签: objective-c c ios comparison double

我有一个存储纬度/经度/海拔高度的对象,需要可靠快速 -hashisEqual实施。我使用double来存储所有基元。

Best practices for overriding isEqual: and hash的已接受答案看起来很不错,但它只涉及integer值。

我的问题是如何处理双打,因为它们不是精确的值。我想比较小数点后8位的基元,这已经比GPS芯片本身准确得多。

这是我到目前为止所提出的,我做得对吗还是需要改进?

我的-isEqual:实施非常简单:

- (BOOL)isEqualToAGPoint:(AGPoint *)otherPoint
{
  if (fabs(otherPoint->latitude - latitude) > 0.00000001)
    return NO;

  if (fabs(otherPoint->longitude - longitude) > 0.00000001)
    return NO;

  if (fabs(otherPoint->altitude - altitude) > 0.00000001)
    return NO;

  return YES;
}

但我对-hash实施不太确定:

- (NSUInteger)hash
{
  NSUInteger prime = 31;
  NSUInteger result = 1;

  result = prime * result + lround(latitude * 100000000);
  result = prime * result + lround(longitude * 100000000);
  result = prime * result + lround(altitude * 100000000);

  return result;
}

快速测试表明它似乎可以正常工作:

// all three have the same longitude and altitude, while a and b have slightly different (but should be considered identical) latitudes, while c's latitude is just different enough to be considered not equal to the a and b
AGPoint *a = [[AGPoint alloc] initWithLatitude:-16.922608127 longitude:145.77124538 altitude:2.74930134];
AGPoint *b = [[AGPoint alloc] initWithLatitude:-16.922608128 longitude:145.77124538 altitude:2.74930134];
AGPoint *c = [[AGPoint alloc] initWithLatitude:-16.922608147 longitude:145.77124538 altitude:2.74930134];

NSLog(@"a == b: %i", (int)[a isEqual:b]);
NSLog(@"a == c: %i", (int)[a isEqual:c]);
NSLog(@"hash for a: %lu  b: %lu c: %lu", (unsigned long)[a hash], (unsigned long)[b hash], (unsigned long)[c hash]);

output:
  a == b: 1
  a == c: 0
  hash for a: 3952407433  b: 3952407433 c: 3952405511

这看起来是否正确?

1 个答案:

答案 0 :(得分:1)

您遇到像(0.5 ± 0.015625)*1e-8这样的问题。坐标的绝对差值小于公差,但舍入导致不同的整数。

修改

这意味着可以认为两个对象相等,但具有不同的哈希码。如果您使用哈希映射,则不一致的相等性和哈希代码可能会造成严重问题。

解决方案是比较isEqual:

中的每个对象的哈希值
- (BOOL)isEqualToAGPoint:(AGPoint *)otherPoint
{
  if ([otherPoint hash] != [self hash])
    return NO;

  if (fabs(otherPoint->latitude - latitude) > 0.00000001)
    return NO;

  if (fabs(otherPoint->longitude - longitude) > 0.00000001)
    return NO;

  if (fabs(otherPoint->altitude - altitude) > 0.00000001)
    return NO;

  return YES;
}