Реализация -hash и -isEqual для класса гео координат - PullRequest
1 голос
/ 01 января 2012

У меня есть объект, который хранит широту / долготу / высоту, и мне нужны надежные и быстрые -hash и isEqual реализации. Я использую double для хранения всех примитивов.

Принятый ответ для Рекомендации по переопределению isEqual: хеш выглядит хорошо, но в нем говорится только о 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 Ответ

1 голос
/ 01 января 2012

У вас проблемы с такими значениями, как (0.5 ± 0.015625)*1e-8. Абсолютная разница координат меньше допуска, но округление приводит к различным целым числам.

EDIT:

Это означает, что два объекта можно считать равными, но они имеют разные хэш-коды. Непоследовательное равенство и хеш-код могут создавать серьезные проблемы, если вы когда-либо используете хеш-карту.

Решение состоит в том, чтобы сравнить хеш каждого объекта внутри 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;
}
...