2- dynamic array data structure

NSMutableArray in Objective-C is a dynamic array, without specifying the length of the array can safely add elements to the inside, you do not need to consider the issue of overflow. Implements dynamic data is very large, for example, a static array package or the expansion list, even a mixture of a variety of ways, to dynamically change their sizes according to the data structure of the data amount. Here it is the easiest to use static data based on dynamic expansion of ways to achieve a dynamic array.

In order to achieve a complete custom data structures, where a static array of package, using a custom static arrays created in the previous article JKRArray.

Dynamic array of custom effects

JKRArrayList *array = [JKRArrayList new];
for (NSUInteger i = 0; i < 60; i++) {
    [array addObject:[Person personWithAge:i]];
}
NSLog(@"添加后 %@", array);

打印:
--- 扩容: 16 -> 24 ---
--- 扩容: 24 -> 36 ---
--- 扩容: 36 -> 54 ---
--- 扩容: 54 -> 81 ---
添加后 size=60, {
<Person: 0x10285ad50>
<Person: 0x10285ae50>
<Person: 0x10285ae70>
...
<Person: 0x10285af30>
<Person: 0x10285af50>
<Person: 0x10285af70>
}


[array removeAllObjects];
NSLog(@"清空后 %@", array);

打印:
<Person: 0x100501070> dealloc
<Person: 0x1005010b0> dealloc
<Person: 0x1005010d0> dealloc  
...
<Person: 0x10285b010> dealloc
清空后 size=0, {

}
复制代码

The basic function of dynamic arrays

Function should provide dynamic array NSMutableArray modeled design, since then also be implemented in a variety of chain dynamic array, there will be so many of the same processing logic and interfaces, where the first base class define a dynamic array:

@interface JKRBaseList<ObjectType> : NSObject {
@protected
    // 记录动态数组的当前长度
    NSUInteger _size;
}

- (NSUInteger)count;
- (void)rangeCheckForAdd:(NSUInteger)index;
- (void)rangeCheckForExceptAdd:(NSUInteger)index;
- (void)addObject:(nullable ObjectType)anObject;
- (BOOL)containsObject:(nullable ObjectType)anObject;
- (nullable ObjectType)firstObject;
- (nullable ObjectType)lastObject;
- (void)removeFirstObject;
- (void)removeLastObject;
- (void)removeObject:(nullable ObjectType)anObject;
- (_Nullable ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(_Nullable ObjectType)obj atIndexedSubscript:(NSUInteger)idx;

@end

@interface JKRBaseList<ObjectType> (JKRBaseList)

- (void)insertObject:(nullable ObjectType)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(nullable ObjectType)anObject;
- (nullable ObjectType)objectAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfObject:(nullable ObjectType)anObject;
- (void)removeAllObjects;
- (void)enumerateObjectsUsingBlock:(void (^)(_Nullable ObjectType obj, NSUInteger idx, BOOL *stop))block;

@end
复制代码

JKRBaseList is the base class for all dynamic array, writing in the extended part of the reason why the inside, in order to facilitate the distinction between the extension is required within the interface Subclasses implement, and the interface is extended beyond the dynamic arrays are the same of different ways the process does not require a subclass overrides, we can achieve as long as JKRBaseList own. The method does not require JKRBaseList implement written on the benefits of the expansion is no specific need to implement an interface written in JKRBaseList.m inside, if it is defined as a method declaration not to come true, the compiler will report a warning. Further written in two parts, which are also easy to distinguish subclasses need to be implemented.

NSArray and NSMutableArray systems allow storage of nil, this function for expansion, so allow incoming data and save it to nil.

First, look at the inside of JKRBaseList member variables: NSUInteger _size; all of this variable is a dynamic array is required, which is responsible for recording the length of the current dynamic array. Because dynamic array of internal and external visible length length is not necessarily true of the same, for example, now we want to achieve through the static arrays dynamic array package, just start initialization when the length of the internal static array of dynamic arrays stored objects may be 16, while the outer display compared with length of 0, because there is not added any element. If a dynamic array of linked lists, the same need to record the length of the array, or each time traversing the list of all nodes to be cumulative length of the array.

The following look at JKRBaseList logic inside do not need a separate sub-class implementation, in order to look at why they are common, and how they are implemented.

Note: Some public interfaces need to call the child class needs to be rewritten interface to achieve specific functions, the first not to consider how to achieve sub-class, sub-class has been achieved assuming that only need to call for a complete interface function.

Implement a common interface

In a method to achieve full JKRBaseList.m, no override subclass.

Get the length of the array

Because the length of a dynamic array is stored in the member variable _size, you only need to return _size

Time complexity: O (1)

- (NSUInteger)count {
    return _size;
}
复制代码

Bounds checking when adding elements

Because dynamic array element may be added to the tail of the characteristics, when the added elements should range bounds checking: index> 0 && index <= _size. index may be equal to the length of the array, this time an additional equivalent of an element in the end of the array.

- (void)rangeCheckForAdd:(NSUInteger)index {
    // index可以等于_size,相当于在数组尾部追加元素
    if (index < 0 || index > _size) {
        [self indexOutOfBounds:index];
    }
}

- (void)indexOutOfBounds:(NSUInteger)index {
    NSAssert(NO, @"index: %zd, size: %zd", index, _size);
}
复制代码

In addition to the added bounds checking

In addition to adding, in other cases, index should be within the operation of the array of the array length: index> 0 && index <_size.

- (void)rangeCheckForExceptAdd:(NSUInteger)index {
    if (index < 0 || index >= _size) {
        [self indexOutOfBounds:index];
    }
}
复制代码

Additional elements of the tail

In the end of the array elements is equivalent to insert an additional element in the position index = _size, interface elements inserted by subclasses.

- (void)addObject:(id)anObject {
    [self insertObject:anObject atIndex:_size];
}
复制代码

It contains elements

Contains elements by looking index of elements, determine whether the find. indexOfObject implemented by subclasses, not found it returns NSUIntegerMax, treatment with NSArray.

- (BOOL)containsObject:(id)anObject {
    return [self indexOfObject:anObject] != NSUIntegerMax;
}
复制代码

Returns the first / last element

Can be obtained according to the interface objectAtIndex, except that when the array length is 0, the direct return nil, instead of calling to objectAtIndex error bounds (the same NSArray).

- (id)firstObject {
    if (_size == 0) {
        return nil;
    }
    return [self objectAtIndex:0];
}

- (id)lastObject {
    if (_size == 0) {
        return nil;
    }
    return [self objectAtIndex:_size - 1];
}
复制代码

Delete the first / last element

Returns the same elements as the first time, when the world returns to zero, it will not give removeObjectAtIndex to deal with cross-border being given to the array length. removeObjectAtIndex implemented by subclasses.

- (void)removeFirstObject {
    if (_size == 0) {
        return;
    }
    [self removeObjectAtIndex:0];
}

- (void)removeLastObject {
    if (_size == 0) {
        return;
    }
    [self removeObjectAtIndex:_size - 1];
}
复制代码

To delete an element

Here are index indexOfObject call calling interface to obtain elements, then call removeObjectAtIndex remove the interface elements.

- (void)removeObject:(id)anObject {
    NSUInteger index = [self indexOfObject:anObject];
    if (index != NSUIntegerMax) {
        [self removeObjectAtIndex:index];
    }
}
复制代码

Support array [] operator

- (id)objectAtIndexedSubscript:(NSUInteger)idx {
    return [self objectAtIndex:idx];
}

// 这里需要区分处理
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx {
    // 如果idx==_size,在数组尾部添加元素
    if (idx == _size) {
        [self insertObject:obj atIndex:idx];
    } else { // 否则替换index位置的元素
        [self replaceObjectAtIndex:idx withObject:obj];
    }
}
复制代码

Subclasses implement requires a separate interface

First, create a subclass JKRArrayList JKRBaseList sequentially perform specific interface implemented in JKRArrayList.m the following:

- (void)insertObject:(nullable ObjectType)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(nullable ObjectType)anObject;
- (nullable ObjectType)objectAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfObject:(nullable ObjectType)anObject;
- (void)removeAllObjects;
- (void)enumerateObjectsUsingBlock:(void (^)(_Nullable ObjectType obj, NSUInteger idx, BOOL *stop))block;
复制代码

Dynamic array variable internal members

This is a dynamic array holds static arrays, and create static initialization time, and specify its length.

#define JKRARRAY_LIST_DEFAULT_CAPACITY (1<<4)

@interface JKRArrayList ()

@property (nonatomic, strong) JKRArray *array;

@end

@implementation JKRArrayList

+ (instancetype)array {
    return [[self alloc] initWithCapacity:JKRARRAY_LIST_DEFAULT_CAPACITY];
}

+ (instancetype)arrayWithCapacity:(NSUInteger)capacity {
    return [[self alloc] initWithCapacity:JKRARRAY_LIST_DEFAULT_CAPACITY];
}

- (instancetype)init {
    return [self initWithCapacity:JKRARRAY_LIST_DEFAULT_CAPACITY];
}

- (instancetype)initWithCapacity:(NSUInteger)capacity {
    self = [super init];
    self.array = [JKRArray arrayWithLength:capacity > JKRARRAY_LIST_DEFAULT_CAPACITY ? capacity : JKRARRAY_LIST_DEFAULT_CAPACITY];
    return self;
}
复制代码

Insert elements

Insert element is not a simple position index corresponding to the static array elements into them simple, suppose we have an array of length 6, to the position 71 inserted into the index 1, as shown below:

If directly substituted into the index 71 for the location of 1, then the array elements becomes:

Such elements did not increase the number of elements in a static array does not change, and the original index is stored in position 32 is missing.

The correct way is to start from the last position to the index position of each element in a backward move, the index vacant position, then the index position into a new element:

code show as below:

// 时间复杂度复杂度O(n) 
// 尾部追加元素复杂度O(1)
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    // 越界检查
    [self rangeCheckForAdd:index];
    // 如果需要扩容则扩充容量
    [self ensureCapacityWithCapacity:_size + 1];
    // 添加元素
    for (NSUInteger i = _size; i > index; i--) {
        self.array[i] = self.array[i - 1];
    }
    self.array[index] = anObject;
    // 添加元素之后,_size要加1
    _size++;
}
复制代码

When adding dynamic elements continue, there will be a static array capacity can not meet the situation, which is the need to expand capacity static arrays, you can start to realize the expansion operation.

Expansion

When an element is inserted when the first need to determine whether the current static array there is sufficient capacity to store the newly added element, the number of current array elements is dynamic _size, it needs to meet its static array length not less than _size + 1, i.e. _array.length> = _size + 1. If the static array capacity is not enough, it is necessary for expansion, expansion way is to create a larger static static data than the current length of the array, the original array and all data copied to the new array, and then add a new element into.

Specific code as follows:

// 时间复杂度复杂度O(n) 
- (void)ensureCapacityWithCapacity:(NSUInteger)capacity {
    NSUInteger oldCapacity = self.array.length;
    if (oldCapacity >= capacity) {
        return;
    }
    NSUInteger newCapacity = oldCapacity + (oldCapacity >> 1);
    NSLog(@"--- 扩容: %zd -> %zd ---", oldCapacity, newCapacity);
    JKRArray *newArray = [JKRArray arrayWithLength:newCapacity];
    for (NSUInteger i = 0; i < _size; i++) {
        newArray[i] = self.array[i];
    }
    self.array = newArray;
}
复制代码

Removing elements

Removing elements also need to move nodes:

// 时间复杂度复杂度O(n) 
// 删除尾部元素复杂度O(1)
- (void)removeObjectAtIndex:(NSUInteger)index {
    [self rangeCheckForExceptAdd:index];
    for (NSUInteger i = index + 1; i < _size; i++) {
        self.array[i - 1] = self.array[i];
    }
    self.array[--_size] = nil;
}
复制代码

Gets index elements

Get need to traverse all the elements of the index node static arrays, equal to find matching elements, and for the return index.

  • The final node traversal should not be a static array length, but the length of _size dynamic array.
  • Since the array of custom allows incoming and saved nil, so it is necessary to find the index nil to do a separate deal, it returns a nil corresponding index.
// 时间复杂度复杂度O(n) 
- (NSUInteger)indexOfObject:(id)anObject {
    if (!anObject) {
        for (NSUInteger i = 0; i < _size; i++) {
            if (self.array[i] == nil) {
                return i;
            }
        }
    } else {
        for (NSUInteger i = 0; i < _size; i++) {
            if ([anObject isEqual:self.array[i]]) {
                return i;
            }
        }
    }
    return NSUIntegerMax;
}
复制代码

Empty Array

// 时间复杂度 O(n)
- (void)removeAllObjects {
    if (_size == 0) return;
    for (NSUInteger i = 0; i < _size; i++) {
        self.array[i] = nil;
    }
    _size = 0;
}
复制代码

Getting an element by index

// 时间复杂度 O(1)
- (id)objectAtIndex:(NSUInteger)index {
    [self rangeCheckForExceptAdd:index];
    return self.array[index];
}
复制代码

Rewrite Print

- (NSString *)description {
    NSMutableString *string = [NSMutableString string];
    [string appendString:[NSString stringWithFormat:@"size=%zd, {\n", _size]];
    [self enumerateObjectsUsingBlock:^(id  _Nullable obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (idx) {
            [string appendString:@"\n"];
        }
        [string appendString:[NSString stringWithFormat:@"%@", obj]];
    }];
    [string appendString:@"\n}"];
    return string;
}
复制代码

Source

Click to view source code

Guess you like

Origin blog.csdn.net/weixin_34255793/article/details/91409720