How to do sparse array in Cocoa

前端 未结 4 918
不知归路
不知归路 2020-12-09 21:38

I have an undetermined size for a dataset based on unique integer keys.

I would like to use an NSMutableArray for fast lookup since all my keys are inte

相关标签:
4条回答
  • 2020-12-09 22:01

    As in Jason's answer, an NSMutableDictionary seems to be the best approach. It adds the overhead of converting the index values to and from NSNumbers, but this is a classic space/time trade off.

    In my implementation I also included an NSIndexSet to make traversing the sparse array much simpler.

    See https://github.com/LavaSlider/DSSparseArray

    0 讨论(0)
  • 2020-12-09 22:03

    Use an NSPointerArray.

    http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSPointerArray_Class/Introduction/Introduction.html

    NSPointerArray is a mutable collection modeled after NSArray but it can also hold NULL values, which can be inserted or extracted (and which contribute to the object’s count). Moreover, unlike traditional arrays, you can set the count of the array directly. In a garbage collected environment, if you specify a zeroing weak memory configuration, if an element is collected it is replaced by a NULL value.

    If you were to use a dictionary like solution, use NSMapTable. It allows integer keys. The NSMutableDictionary based solution recommended has a tremendous amount of overhead related to all of the boxing & unboxing of integer keys.

    0 讨论(0)
  • 2020-12-09 22:08

    I have to disagree with bbum's answer on this. A NSPointerArray is an array, not a sparse array, and there are important differences between the two.

    I strongly recommend that bbums solution not be used.

    The documentation for NSPointerArray is available here.

    Cocoa already has an array object as defined by the NSArray class. NSPointerArray inherits from NSObject, so it is not a direct subclass of NSArray. However, the NSPointerArray documentation defines the class as such:

    NSPointerArray is a mutable collection modeled after NSArray but it can also hold NULL values

    I will make the axiomatic assumption that this definition from the documentation asserts that this is a "logical" subclass of NSArray.

    Definitions-

    A "general" array is: a collection of items, each of which has a unique index number associated with it.

    An array, without qualifications, is: A "general" array where the indexes of the items have the following properties: Indexes for items in the array begin at 0 and increase sequentially. All items in the array contains an index number less than the number of items in the array. Adding an item to an array must be at index + 1 of the last item in the array, or an item can be inserted in between two existing item index numbers which causes the index number of all subsequent items to be incremented by one. An item at an existing index number can be replaced by another item and this operation does not change the index numbers of the existing operations. Therefore, insert and replace are two distinct operations.

    A sparse array is: A "general" array where the index number of the first item can begin at any number and the index number of subsequent items added to the array has no relation to or restrictions based on other items in the array. Inserting an item in to a sparse array does not effect the index number of other items in the array. Inserting an item and replacing an item are typically synonymous in most implementations. The count of the number of items in the sparse array has no relationship to the index numbers of the items in the sparse array.

    These definitions make certain predictions about the behavior of a "black box" array that are testable. For simplicity, we'll focus on the following relationship:

    In an array, the index number of all the items in the array is less than the count of the number of items in the array. While this may be true of a sparse array, it is not a requirement.

    In a comment to bbum, I stated the following:

    a NSPointerArray is not a sparse array, nor does it behave like one. You still have to fill all the unused indexes with NULL pointers. Output from [pointerArray insertPointer:@"test" atIndex:17]; on a freshly instantiated NSPointerArray:

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSConcretePointerArray insertPointer:atIndex:]: attempt to insert pointer at index 17 beyond bounds 0'

    It is stated, without proving, the the behavior of NSPointerArray above violates the very definition of a sparse array. This part of the error message is revealing: attempt to insert pointer at index 17 beyond bounds 0', in particular the part about having to add the first new item at index 0.

    bbum then comments:

    That is incorrect. You failed to call -setCount: to set the capacity to a sufficient size.

    It is non-sensical to "set the count" of the number of items in a sparse array. If NSPointerArray was a sparse array, one would expect that after adding the first item at index 17, the count of the number of items in the NSPointerArray would be one. However, following bbums advice, the number of items in the NSPointerArray after adding the first items is 18, not 1.

    QED- It is shown that a NSPointerArray is in fact an array, and for the purposes of this discussion, a NSArray.

    Additionally, bbum makes the following additional comments:

    NSPointerArray most certainly does support holes.

    This is provably false. An array requires all items contained in it to contain something, even if that something is 'nothing'. This is not true of a sparse array. This is the very definition of a 'hole' for the purposes of this discussion. A NSPointerArray does not contain holes in the sparse array sense of the term.

    That was one of the whole points of writing the class. You have to set the count first.

    It is provably non-sensical to "set the count" of a sparse array.

    Whether the internal implementation is a sparse array or a hash or, etc, is an implementation detail.

    This is true. However, the documentation for NSPointerArray does not make any reference to how it implements or manages its array of items. Furthermore, it does not state anywhere that a NSPointerArray "efficiently manages an array of NULL pointers."

    QED- bbum is depending on the undocumented behavior that a NSPointerArray efficiently handles NULL pointers via a sparse array internally. Being undocumented behavior, this behavior can change at any time, or may not even apply to all uses of the NSPointerArray. A change in this behavior would be catastrophic if the highest index number stored in it are sufficiently large (~ 2^26).

    And, in fact, it is not implemented as one big hunk of memory...

    Again, this is a private implementation detail that is undocumented. It is extremely poor programming practice to depend on this type of behavior.

    0 讨论(0)
  • 2020-12-09 22:21

    It sounds like your needs would be better met with an NSMutableDictionary. You will need to wrap the ints into NSNumber objects as follows:

    -(void)addItem:(int)key value:(id)obj
    {
        [data setObject:obj forKey:[NSNumber numberWithInt:key]];
    }
    
    -(id)getItem:(int)key
    {
        return [data objectForKey:[NSNumber numberWithInt:key]];
    }
    

    There's no easy was to enlarge the size of an NSMutableArray, since you cannot have nil objects in the in-between slots. You can, however, use [NSNull null] as a 'filler' to create the appearance of a sparse array.

    0 讨论(0)
提交回复
热议问题