this is actually a question that i'd been trying to solve...
i need to implement this functionality in NSSet...
I know how hash tables and sets works.. and NSSet seems to store the pointer to the objects inside the hash table using the HASH as the index of that array... when more than one object falls in that hash.. it uses isEqual to detect which one of the objects is the member we search... that means...
HASH value => gives the index of the array of pointers in the hash table, and each one of those pointers points to an array (or some collection) that holds the objects with that hash (as it iterates over it to detect which object is the member)... this is a fairly common data struct...
My question is... is there a way to retrieve the array of objects that is being pointed by the hash table... i need ALL the objects that have the SAME HASH VALUE inside an NSSet...
i need this in order to quickly process proximity between points...
Is there a way? i know i can use a predicate using
[NSPredicate predicateWithFormat:#"hash == %u",hash];
but this uses an enumeration and is not as fast as it needs to be (real fast)
Is there a way or should i create a HASH TABLE from scratch? (or use core foundation)
Thanks and sorry for the trouble!
if you want the object in an array just call -allObjects, if you want all hashes, then you will have to iterate through them, because they are longs and can't be stored in an NSArray directly.
I had the idea to make a mock object that overrides its own hash, then you could search through an array for the index of this object that is pretending to be your object.
#interface MockHasher : NSObject{
NSUInteger mockHash;
}
#property(assign,nonatomic,getter = hash,setter = setHash:)NSUInteger mockHash;
#end
#implementation MockHasher
#synthesize mockHash;
-(BOOL)isEqual:(id)object{return YES;}
-(BOOL)isEqualTo:(id)object{return YES;}
#end
example:
NSSet * myset = [NSSet setWithObject:#(1)];
MockHasher * mockObject = [[MockHasher new] autorelease];
mockObject.hash = #(1).hash;
NSArray * allObjects = [myset allObjects];
NSUInteger i = [allObjects indexOfObject:mockObject];
id result = [allObjects objectAtIndex:i];
NSLog(#"result = %#",result);
It is fragile, because it is depending on the array asking the object passed in for isEqual: rather than asking the iterated object... I don't know how reliable this is... but it worked in my test.
As of iOS 6.0 and MacOS 10.5, you now have an actual NSHashTable object to work with. Its modeled after NSSet, but instead is it's own thing. Here's some additional Apple documentation on Hash Tables, as well.
Related
NSDictionary keys are id<NSCopying> but the value for a set is just id, and the docs indicate their values are retained. According to the Set Fundamentals of the Collection Programming Topics docs:
You can, however, modify individual objects themselves (if they support modification).
If you modify an object, this could affect the hashvalue of the object, which would affect lookups. I assumed that an NSSet is a fast lookup?
Here's an example that shows how things break if you mutate objects:
NSMutableString *str = [NSMutableString stringWithString: #"AWESOME"];
NSCountedSet *countedSet = [[NSCountedSet alloc] init];
[countedSet addObject: str];
[countedSet addObject: str];
NSLog(#"%#", #([countedSet countForObject: #"AWESOME"]));
[str appendString: #" NOT AWESOME"];
NSLog(#"%#", #([countedSet countForObject: #"AWESOME NOT AWESOME"]));
NSLog(#"%#", #([countedSet countForObject: #"AWESOME"]));
NSLog(#"%#", #([countedSet countForObject: str]));
for(NSString *s in countedSet) {
NSLog(#"%# - %#", str, #([countedSet countForObject: s]));
}
NSSet *set = [NSSet setWithArray: #[ str ]];
NSLog(#"Set Contains string, %#", #([set containsObject: str]));
[str appendString: #"asdf"];
NSLog(#"Set Contains string, %#", #([set containsObject: str]));
NSLog(#"%#", set);
And output with my interpretation:
[64844:303] 2 // Count is 2
[64844:303] 0 // Count should be 2 - if it looks for the literal string
[64844:303] 0 // Count should be 0, but can't find original object either
[64844:303] 0 // Count should be 2 - asking for actual object that's in there
[64844:303] AWESOME NOT AWESOME - 0 // Should be 2 - asking for actual object that it just retrieved
[64844:303] Set Contains string, 1 // Correct, pre-mutation
[64844:303] Set Contains string, 0 // Should be true, object is in there
[65070:303] {(
"AWESOME NOT AWESOMEasdf" // see? It's in there
)}
My take:
The set likely buckets based on hash value, when the hash is changed out behind the set, it doesn't know what to do and lookups are broken. The documentation is lacking in this area.
My question restated:
Docs say you can mutate objects, which is not intuitive.
Mutating objects breaks sets.
WTF?
That line from the docs is confusing. However, note that three paragraphs down it goes on to say:
If mutable objects are stored in a set, either the hash method of the
objects shouldn’t depend on the internal state of the mutable objects
or the mutable objects shouldn’t be modified while they’re in the set.
For example, a mutable dictionary can be put in a set, but you must
not change it while it is in there. (Note that it can be difficult to
know whether or not a given object is in a collection).
What your code is demonstrating is a known property of the hash-based collection classes. It can affect dictionaries, too, if a key object is implemented such that copying returns the original, which is inherently mutable.
There's no real way to test if an object is mutable. So, it can't force immutability.
Also, as alluded to in the quote above, it's possible to make a mutable class whose hash and equality are not affected by mutations.
Finally, it would too severely limit the utility of those collection classes if they could only be used with copyable classes and made copies of the elements (like dictionaries make copies of their keys). The collections are used to represent relationships, among other things, and it wouldn't do if you tried to establish a relationship between objects but instead established a relationship to a separate copy.
Since the only reliable way of ensuring an object's immutability in Objective-C is to make a copy, Cocoa designers had two choices:
Make NSSet copy the objects - That would be safe, bit it would severely restrict the use of NSSet due to increased memory usage.
Use retained objects - That would keep memory usage to a bare minimum, but it would give the users a way to shoot themselves in a foot by mutating an object inside NSSet.
Designers picked the second approach over the first one, because it fixes a danger that could be avoided by proper coding technique. In contrast, selecting the first approach would be "binding" on everybody, in the sense that inserting a new object would always make a copy.
Currently, users have a choice of inserting copies of objects that they create manually, thus emulating the first approach. However, an implementation that forces a copy cannot emulate an implementation that retains objects, making it a less flexible choice.
I have a list of long values:
300210, 300211, 310210, 310211, ...
I'm looking for the best way to check whether a number is present in a collection. The collection is non mutable and this check can possibly happen hundreds of time per second (it's part of a physics engine collision presolving).
If using an NSArray, I'm to use NSNumbers. These are objects - Is the containsObject: method using hashcodes? Or does it consistently use a value comparison (rather than pointer address)?
How about NSSet? I know it has a member: method to use isEqual: but no practical experience with it.
thanks for your help find the best way to address this.
I would suggest turn on objective-C++ and use std::set. It's much faster then NSSet.
You will need:
in header:
#include <set>
using namespace std;
in code:
set<int> numberCollection;
Using NSArray, if the array contains NSNumber then you can use containsObject:
as they match with the value not with the pointers.
NSNumber *num3 = [NSNumber numberWithInteger:3];
NSArray *array = #[#1, #2, num3, #4, #5];
BOOL isExists = [array containsObject:#3]; // yes
Also with NSSet you can do similar way:
NSSet *set = [NSSet setWithArray:array];
BOOL isExists = [set containsObject:#3];
You can read a great article about Objective-C collections and their performance at objc.ico.
In your case (checking if a collection contains given object) the NSSet is definitely the best choice.
NSSet and its mutable variant NSMutableSet are an unordered collection of objects. Checking for existence is usually an O(1) operation, making this much faster for this use case than NSArray.
I need a NSDictionary object that not only support key->value storage but also support key sequence just like Array does.
Is there something exists on Objective-C ?
Monolo's answer is the right way to do this.
[self allKeys] does not return ordered sequence. it's an NSSet style.
And I write a Class named PLHash that has many features:
key value storage
key order support
items max size control ( set a maxItems to PLHash and it will keep items by FIFO )
here is the URL
https://github.com/xhan/PlutoLand/blob/master/PlutoLand/Additions/PLHash.h
I have found this article (and the linked source code) useful: http://cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html
Nothing built in, but you can use an array of keys along side the dictionary to record the order.
You would be best off either getting the allkeys array and sorting that then getting the value for a key or making your own class to store objects the way you want them. It really sounds like you are doing something NSDictionary really wasn't built to do, what are you trying to do that you want the keys sorted?
One option is to create a category on nsdictionary, call it sortedKeys or something, which returns a sorted [self allKeys].
Update: Now that I'm not responding from my phone, here's my suggestion, written verbosely for clarity, and assuming you're using ARC:
#interface NSDictionary (sort)
- (NSArray *)sortedKeys;
#end
#implementation NSDictionary (sort)
- (NSArray *)sortedKeys {
NSArray *keyArray = [self allKeys];
NSArray *sortedKeyArray = [keyarray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
return sortedKeyArray;
}
#end
I personally don't think that subclassing NSDictionary is the way to go.
I need to detect change in NSArray object - that is if some object was added/removed to/from NSArray or was just edited in-place. Are there some integrated NSArray hash functions for this task - or I need to write my own hashing function for NSArray ? Maybe someone has different solution ? Any ideas ?
All objects have a -hash method but not all objects have a good implementation.
NSArray's documentation doesn't define it's result, but testing reveals it returns the length of the array - not very useful:
NSLog(#"%lu", #[#"foo"].hash); // output: 1
NSLog(#"%lu", #[#"foo", #"bar"].hash); // output: 2
NSLog(#"%lu", #[#"hello", #"world"].hash); // output: 2
If performance isn't critical, and if the array contains <NSCoding> objects then you can simply serialise the array to NSData which has a good -hash implementation:
[NSArchiver archivedDataWithRootObject:#[#"foo"]].hash // 194519622
[NSArchiver archivedDataWithRootObject:#[#"foo", #"bar"]].hash // 123459814
[NSArchiver archivedDataWithRootObject:#[#"hello", #"world"]].hash // 215474591
For better performance there should be an answer somewhere explaining how to write your own -hash method. Basically call -hash on every object in the array (assuming the array contains objects that can be hashed reliably) and combine each together mixed in with some simple randomising math.
You could use an NSArrayController, which is Key-Value-Observing compliant. Unfortunately NSArray is only KVC compliant. This way you can easily monitor the array controller's arrangedObjects property. This should solve your problem.
Also, see this question: Key-Value-Observing a to-many relationship in Cocoa
I want to create an NSArray with objects of the same value (say NSNumber all initialized to 1) but the count is based on another variable. There doesn't seem to be a way to do this with any of the intializers for NSArray except for one that deals with C-style array.
Any idea if there is a short way to do this?
This is what I am looking for:
NSArray *array = [[NSArray alloc] initWithObject:[NSNumber numberWithInt:0]
count:anIntVariable];
NSNumber is just one example here, it could essentially be any NSObject.
The tightest code I've been able to write for this is:
id numbers[n];
for (int x = 0; x < n; ++x)
numbers[x] = [NSNumber numberWithInt:0];
id array = [NSArray arrayWithObjects:numbers count:n];
This works because you can create runtime length determined C-arrays with C99 which Xcode uses by default.
If they are all the same value, you could also use memset (though the cast to int is naughty):
id numbers[n];
memset(numbers, (int)[NSNumber numberWithInt:0], n);
id array = [NSArray arrayWithObjects:numbers count:n];
If you know how many objects you need, then this code should work, though I haven't tested it:
id array = [NSArray arrayWithObjects:(id[5]){[NSNumber numberWithInt:0]} count:5];
I can't see any reason why this structure in a non-mutable format would be useful, but I am certain that you have your reasons.
I don't think that you have any choice but to use a NSMutableArray, build it with a for loop, and if it's really important that the result not be mutable, construct a NSArray and use arrayWithArray:
I agree with #mmc, make sure you have a valid reason to have such a structure (instead of just using the same object N times), but I'll assume you do.
There is another way to construct an immutable array which would be slightly faster, but it requires creating a C array of objects and passing it to NSArray's +arrayWithObject:count: method (which returns an autoreleased array, mind you) as follows:
id anObject = [NSNumber numberWithInt:0];
id* buffer = (id*) malloc(sizeof(id) * anIntVariable);
for (int i = 0; i < anIntVariable; i++)
buffer[i] = anObject;
NSArray* array = [NSArray arrayWithObjects:buffer count:anIntVariable];
free(buffer);
You could accomplish the same thing with even trickier pointer math, but the gains are fairly trivial. Comment if you're interested anyway.
Probably the reason there is no such method on NSArray is that the semantics are not well defined. For your case, with an immutable NSNumber, then all the different semantics are equivalent, but imagine if the object you were adding was a mutable object, like NSMutableString for example.
There are three different semantics:
retain — You'd end up with ten pointers to the same mutable string, and changing any one would change all ten.
copy — You'd end up with ten pointers to the same immutable string, or possibly ten different pointers to immeduable strings with the same value, but either way you'd not be able to change any of them.
mutableCopy — You'd end up with ten different mutable string objects, any of which you could change independently.
So Apple could write three variants of the method, or have some sort of parameter to control the semantics, both of which are ugly, so instead they left it to you to write the code. If you want, you can add it as an NSArray category method, just be sure you understand the semantic options and make it clear.
The method:
-(id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
has this same issue.
Quinn's solution using arrayWithObjects:count: is a reasonably good one, probably about the best you can get for the general case. Put it in an NSArray category and that's about as good as it is going to get.