*[NSMutableSet addObject: X]* 添加对象 X,即使 *[Y isEqual:X]* returns TRUE 对于已经在集合中的对象 Y
*[NSMutableSet addObject: X]* adds object X even though *[Y isEqual:X]* returns TRUE for an object Y already in the Set
我有一个名为 FactorHelper
的 Objective-C class,其定义如下。它有一个 属性 称为因子,它是 NSNumbers
的 NSMutableArray
。我在此 class 中有一个自定义 isEqual:
方法,如果两个 FactorHelper
对象中的因子 属性 具有相同的数字(即使数字顺序不同)。
我试图通过创建两个 FactorHelper
对象来进行测试 一个对象为 10,5,2 并且 另一个对象为 10,2,5。然后我创建了一个 NSMutableSet
,添加了第一个对象,然后添加了第二个对象。我原以为不会添加第二个对象,但我看到它已添加。当我单步执行代码时,我发现 addObject 正在调用 isEqual
并返回 TRUE
。我做错了什么?
更新
将 [NSMutableSet new]
更改为 [NSMutableSet alloc] init]
让事情按预期工作。
此外,更改所有 TRUE、FALSE isEqual 为 YES、NO 使其行为正确(即使我将其保持为 [NSMutableSet new]
)。
我不知道发生了什么。有人可以解释一下吗?!
Class定义
@interface FactorHelper: NSObject
@property NSMutableArray <NSNumber *> *factors;
-(BOOL) isEqual:(FactorHelper *)other;
-(instancetype) initWithFactors:(NSMutableArray *)factors;
-(NSString *) description;
@end
@implementation FactorHelper
- (instancetype) initWithFactors:(NSMutableArray *)factors
{
self = [super init];
if (self) {
_factors = factors;
}
return self;
}
-(BOOL) isEqual:(FactorHelper *)other
{
if ([self.factors count] != [other.factors count])
{
return FALSE;
}
else
{
NSMutableDictionary <NSNumber *, NSNumber *> *myHashTable = [[NSMutableDictionary alloc] init];
for (NSNumber *nextNumber in self.factors) {
if(myHashTable[nextNumber] == nil)
{
myHashTable[nextNumber] = @(1);
}
else
{
myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue]+1);
}
}
for (NSNumber *nextNumber in other.factors)
{
if(myHashTable[nextNumber] == nil)
{
return FALSE;
}
else
{
myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue] - 1);
if ([myHashTable[nextNumber] integerValue] == 0) {
[myHashTable removeObjectForKey:nextNumber];
}
}
}
if ([[myHashTable allKeys] count] == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
}
@end
单元测试代码
NSMutableSet *testSet = [NSMutableSet new];
FactorHelper *fact1 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(5),@(2)] mutableCopy]];
FactorHelper *fact2 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(2),@(5)] mutableCopy]];
[testSet addObject:fact1];
[testSet addObject:fact2];
NSLog(@"Are factors 1 and 2 the same: %d",[fact1 isEqual:fact2]);
您的代码从未工作,即使有时看起来如此。
问题是 isEqual
的自定义实现并不是使 class 在 Set 中工作的唯一要求。想一想:什么是一个集合?它是一个 散列 table。因此,您还必须提供 hash
的匹配自定义实现 — 而您还没有这样做。
散列性的要求是:如果两个对象相等,则两个对象的散列值必须相同。
NSMutableSet
是一个基于散列值的集合。您需要重写 hash
方法,使其元素类型与 isEqual:
.
一致
在你的情况下,是这样的:
- (NSUInteger)hash {
NSCountedSet *factorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
return [@"FactorHelper" hash] + [factorCounts hash];
}
我不确定你是如何检查的我看到它被添加了,但这使得你的 FactorHelper
与 NSMutableSet
一起工作。
顺便说一下,您的 isEqual:
可以利用 NSCountedSet
来实现更短一些。
-(BOOL) isEqual:(FactorHelper *)other {
NSCountedSet *myFactorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
NSCountedSet *otherFactorCounts = [[NSCountedSet alloc] initWithArray:other.factors];
return [myFactorCounts isEqual:otherFactorCounts];
}
这与上面的 hash
更加一致。
我有一个名为 FactorHelper
的 Objective-C class,其定义如下。它有一个 属性 称为因子,它是 NSNumbers
的 NSMutableArray
。我在此 class 中有一个自定义 isEqual:
方法,如果两个 FactorHelper
对象中的因子 属性 具有相同的数字(即使数字顺序不同)。
我试图通过创建两个 FactorHelper
对象来进行测试 一个对象为 10,5,2 并且 另一个对象为 10,2,5。然后我创建了一个 NSMutableSet
,添加了第一个对象,然后添加了第二个对象。我原以为不会添加第二个对象,但我看到它已添加。当我单步执行代码时,我发现 addObject 正在调用 isEqual
并返回 TRUE
。我做错了什么?
更新
将 [NSMutableSet new]
更改为 [NSMutableSet alloc] init]
让事情按预期工作。
此外,更改所有 TRUE、FALSE isEqual 为 YES、NO 使其行为正确(即使我将其保持为 [NSMutableSet new]
)。
我不知道发生了什么。有人可以解释一下吗?!
Class定义
@interface FactorHelper: NSObject
@property NSMutableArray <NSNumber *> *factors;
-(BOOL) isEqual:(FactorHelper *)other;
-(instancetype) initWithFactors:(NSMutableArray *)factors;
-(NSString *) description;
@end
@implementation FactorHelper
- (instancetype) initWithFactors:(NSMutableArray *)factors
{
self = [super init];
if (self) {
_factors = factors;
}
return self;
}
-(BOOL) isEqual:(FactorHelper *)other
{
if ([self.factors count] != [other.factors count])
{
return FALSE;
}
else
{
NSMutableDictionary <NSNumber *, NSNumber *> *myHashTable = [[NSMutableDictionary alloc] init];
for (NSNumber *nextNumber in self.factors) {
if(myHashTable[nextNumber] == nil)
{
myHashTable[nextNumber] = @(1);
}
else
{
myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue]+1);
}
}
for (NSNumber *nextNumber in other.factors)
{
if(myHashTable[nextNumber] == nil)
{
return FALSE;
}
else
{
myHashTable[nextNumber] = @([myHashTable[nextNumber] integerValue] - 1);
if ([myHashTable[nextNumber] integerValue] == 0) {
[myHashTable removeObjectForKey:nextNumber];
}
}
}
if ([[myHashTable allKeys] count] == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
}
@end
单元测试代码
NSMutableSet *testSet = [NSMutableSet new];
FactorHelper *fact1 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(5),@(2)] mutableCopy]];
FactorHelper *fact2 = [[FactorHelper alloc] initWithFactors:[@[@(10),@(2),@(5)] mutableCopy]];
[testSet addObject:fact1];
[testSet addObject:fact2];
NSLog(@"Are factors 1 and 2 the same: %d",[fact1 isEqual:fact2]);
您的代码从未工作,即使有时看起来如此。
问题是 isEqual
的自定义实现并不是使 class 在 Set 中工作的唯一要求。想一想:什么是一个集合?它是一个 散列 table。因此,您还必须提供 hash
的匹配自定义实现 — 而您还没有这样做。
散列性的要求是:如果两个对象相等,则两个对象的散列值必须相同。
NSMutableSet
是一个基于散列值的集合。您需要重写 hash
方法,使其元素类型与 isEqual:
.
在你的情况下,是这样的:
- (NSUInteger)hash {
NSCountedSet *factorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
return [@"FactorHelper" hash] + [factorCounts hash];
}
我不确定你是如何检查的我看到它被添加了,但这使得你的 FactorHelper
与 NSMutableSet
一起工作。
顺便说一下,您的 isEqual:
可以利用 NSCountedSet
来实现更短一些。
-(BOOL) isEqual:(FactorHelper *)other {
NSCountedSet *myFactorCounts = [[NSCountedSet alloc] initWithArray:self.factors];
NSCountedSet *otherFactorCounts = [[NSCountedSet alloc] initWithArray:other.factors];
return [myFactorCounts isEqual:otherFactorCounts];
}
这与上面的 hash
更加一致。