iOS 数据结构之队列
队列是iOS 中常见的一种数据结构,比如 NSOpeartionQueue 和 GCD 的 各种队列 ,其特点是先进先出(First In First out), 在多线程执行执行多个任务时候 ,放进同一队列的任务是顺次从队列里取出任务并执行的.
gith 代码仓库 : https://github.com/ZhaoBingDong/iOS-DataStructures.git
队列在我们生活中也很常见,比如排队购票 去银行办理业务排队办理业务,都是队首的办理完业务后,离开柜台 下一个人接着办理业务
本文我们将介绍两种队列实现方式, 数组队列和循环队列.
数组队列是利用我们已经写好的数据结构 ArrayList 来存放元素,每次入队时候 往数组后边添加一个元素,每次出队时候从数组第0个元素位置出队一个元素,但是用数组队列其缺点是 在出队时候 数组里所有元素都要整体往前移动1个位置 .
数组和循环队列所有方法如下
+ (instancetype) arrayQueue;
+ (instancetype) arrayQueueWithCapacity:(NSInteger)capacity;
– (instancetype) initWithCapacity:(NSInteger)capacity;
– (void) enqueue:(ObjectType)obj; ///入队列
– (id) dequeue; ///出队列
– (void) removeAllObjects; ///移除队列里边所有元素
///firstObject
@property (nonatomic,weak) ObjectType firstObject;
///size
@property (nonatomic,assign,readonly) NSInteger size;
///isEmpty
@property (nonatomic,assign,getter=isEmpty) BOOL empty;
#import “ArrayList.h”
#import “ArrayQueue.h”
@interface ArrayQueue ()
{
ArrayList *_array;
}
@end
@implementation ArrayQueue
+ (instancetype)arrayQueue {
return [[ArrayQueue alloc] initWithCapacity:10];
}
+ (instancetype)arrayQueueWithCapacity:(NSInteger)capacity {
return [[ArrayQueue alloc] initWithCapacity:capacity];
}
– (instancetype)initWithCapacity:(NSInteger)numItems {
if (self = [super init]) {
_array = [ArrayList arrayWithCapacity:numItems];
}
return self;
}
– (void)enqueue:(id)obj {
[_array addObject:obj];
}
– (id)dequeue {
[_array removeObjectAtIndex:0];
return nil;
}
– (id)firstObject {
return [_array firstObject];
}
– (void)removeAllObjects {
[_array removeAllObjects];
}
– (NSInteger)size {
return _array.count;
}
– (BOOL)isEmpty {
return _array.count == 0;
}
– (NSString *)description {
NSMutableString *res = [NSMutableString string];
[res appendFormat:@”ArrayQueue: %p \n”,self];
[res appendString:@”front [ “];
for (int i = 0; i<_array.count; i++) {
id object = [_array objectAtIndex:i];
[res appendFormat:@”%@”,object];
if (i != _array.count – 1) {
[res appendString:@” , “];
}
}
[res appendString:@” ] tail “];
return res;
}
– (void)dealloc
{
}
@end
循环队列当出队一个元素后,不需要队列里所有元素往前移动,只需要移动队列 _front 和 _tail 指向的位置 , 可以复用已经出队元素留下的内存空间 ,类似与一个环形 , 其内部实现也是一个数组用来存放入队的元素, 当入队元素个数小于数组元素时 就会将元素放到一个空的内存空间位置,只需要维护下_front 和 _tail 指向位置 用来区分队首和队尾
#import “LoopQueue.h”
static NSInteger const defaultCapacity = 10;
typedef void * AnyObject;
@interface LoopQueue ()
{
@private
AnyObject *_array;
NSInteger _front , _tail;
NSInteger _size;
}
///capacity
@property (nonatomic,assign) NSInteger capacity;
@end
@implementation LoopQueue
– (instancetype)initWithCapacity:(NSInteger)capacity {
if (self = [super init]) {
_capacity = capacity + 1;
size_t size = sizeof(AnyObject) * _capacity;
_array = calloc(_capacity, size);
_front = 0;
_size = 0;
_tail = 0;
}
return self;
}
+ (instancetype)loopQueue {
return [[LoopQueue alloc] initWithCapacity:defaultCapacity];
}
+ (instancetype)loopQueueWithCapacity:(NSInteger)capacity {
return [[LoopQueue alloc] initWithCapacity:capacity];
}
– (id)dequeue {
if (self.isEmpty) {
@throw [NSException exceptionWithName:@”queue empty” reason:@”Cannot dequeue from an empty queue.” userInfo:nil];
return nil;
}
id object = (__bridge_transfer id)(_array[_front]);
_array[_front] = NULL;
_front = (_front + 1) % (_size);
_size–;
if (_size == self.size * 0.25 && self.size * 0.5 != 0) {
[self resize:self.size * 0.5];
}
return object;
}
– (void)enqueue:(id)obj {
if (!obj) {
@throw [NSException exceptionWithName:@”Object empty” reason:@”Object cannot a null object” userInfo:nil];
return;
}
if ((_tail + 1) % self.capacity == _front ) {
[self resize:self.capacity * 2];
}
_array[_tail] = (__bridge_retained AnyObject)obj;
_tail = (_tail + 1) % self.capacity;
_size ++;
}
– (void)removeAllObjects {
}
/**
对数组扩容
@param capacity 新的容量
*/
– (void) resize:(NSInteger)capacity {
_capacity = capacity + 1;
AnyObject *oldArray = _array;
AnyObject *newArray = calloc(_capacity,sizeof(AnyObject));
size_t size = sizeof(AnyObject) * self.size;
memcpy(newArray,oldArray,size); ///对旧的数组进行值的拷贝
_array = newArray;
_front = 0;
_tail = self.size;
if (oldArray != NULL) {
free(oldArray);
oldArray = NULL;
}
}
– (NSInteger)size {
return _size;
}
– (BOOL)isEmpty {
return (_front == _tail);
}
– (id)firstObject {
if (self.isEmpty) {
@throw [NSException exceptionWithName:@”queue empty” reason:@”Cannot dequeue from an empty queue.” userInfo:nil];
return nil;
}
AnyObject obj = _array[_front];
return (__bridge id)obj;
}
– (NSString *)description {
NSMutableString *res = [NSMutableString string];
[res appendFormat:@”ArrayQueue: %p \n”,self];
[res appendString:@”front [ “];
for (NSInteger i = 0; i <self.size;i++) {
id object = (__bridge id)(_array[i]);
if (!object) continue;
[res appendFormat:@”%@”,object];
if (i < self.size – 1) {
[res appendString:@” , “];
continue;
}
}
[res appendString:@” ] tail “];
return res;
}
– (void)dealloc
{
if (_array != NULL) {
NSInteger i = self.capacity – 1;
while (i >= 0) {
AnyObject *obj = _array[i];
if (obj != NULL)
CFRelease(obj);
i–;
}
free(_array);
}
}
@end
测试用例 由于数组队列需要出队和入队时候需要频繁的移动数组里元素索引位置 ,这个操作时耗时的 ,而循环队列只需要把新入队的元素 存放在一个空的位置即可 ,减少了对数组内部元素的移动的操作.因此循环队列比数组队列时间耗时更少 性能更好
(void) testStack {
_timeArray = [NSMutableArray array];
///10万次对比 NSMutableArray 和 ArrayList
int number = 100000;
Person *p = [Person new];
for (int i = 0; i<10; i++) {
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
ArrayQueue