我有一个存储纬度/经度/高度的对象,需要可靠而快速的-hash
和isEqual
实现。我使用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
这看起来正确吗?
最佳答案
你遇到了像(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;
}