我想在我的 Objective-C 程序中使用队列数据结构。在 C++ 中,我会使用 STL 队列。Objective-C 中的等效数据结构是什么?如何推送/弹出项目?
10 回答
Ben 的版本是堆栈而不是队列,所以我对其进行了一些调整:
NSMutableArray+QueueAdditions.h
@interface NSMutableArray (QueueAdditions)
- (id) dequeue;
- (void) enqueue:(id)obj;
@end
NSMutableArray+QueueAdditions.m
@implementation NSMutableArray (QueueAdditions)
// Queues are first-in-first-out, so we remove objects from the head
- (id) dequeue {
// if ([self count] == 0) return nil; // to avoid raising exception (Quinn)
id headObject = [self objectAtIndex:0];
if (headObject != nil) {
[[headObject retain] autorelease]; // so it isn't dealloc'ed on remove
[self removeObjectAtIndex:0];
}
return headObject;
}
// Add to the tail of the queue (no one likes it when people cut in line!)
- (void) enqueue:(id)anObject {
[self addObject:anObject];
//this method automatically adds to the end of the array
}
@end
只需将 .h 文件导入您想使用新方法的任何位置,然后像调用任何其他 NSMutableArray 方法一样调用它们。
祝你好运,继续编码!
我不会说使用 NSMutableArray 一定是最好的解决方案,特别是如果您要添加带有类别的方法,因为如果方法名称发生冲突,它们可能会导致脆弱性。对于 quick-n-dirty 队列,我将使用在可变数组末尾添加和删除的方法。但是,如果您打算重用队列,或者如果您希望您的代码更具可读性和不言而喻,那么您可能想要一个专用的队列类。
Cocoa 没有内置的,但还有其他选项,您也不必从头开始编写。对于仅从末端添加和删除的真正队列,循环缓冲区数组是一种非常快速的实现。查看CHDataStructures.framework,这是我一直在研究的 Objective-C 库/框架。它有多种队列实现,以及堆栈、双端队列、排序集等。对于您的目的,CHCircularBufferQueue比使用 NSMutableArray 更快(即可以通过基准测试证明)和更具可读性(诚然是主观的)。
使用原生 Objective-C 类而不是 C++ STL 类的一大优势是它与 Cocoa 代码无缝集成,并且在编码/解码(序列化)方面工作得更好。它还可以完美地与垃圾收集和快速枚举配合使用(两者都存在于 10.5+ 中,但只有后者在 iPhone 上)并且您不必担心什么是 Objective-C 对象以及什么是 C++ 对象。
最后,虽然 NSMutableArray 在从任一端添加和删除时优于标准 C 数组,但它也不是队列的最快解决方案。对于大多数应用程序来说它是令人满意的,但是如果您需要速度,循环缓冲区(或在某些情况下为保持高速缓存行热而优化的链表)可以轻松击败 NSMutableArray。
据我所知,Objective-C 不提供 Queue 数据结构。您最好的选择是创建一个NSMutableArray
, 然后使用[array lastObject]
,[array removeLastObject]
来获取项目,然后[array insertObject:o atIndex:0]
...
如果您经常这样做,您可能希望创建一个 Objective-C 类别来扩展NSMutableArray
该类的功能。类别允许您动态地将函数添加到现有类(即使是那些您没有源代码的类) - 您可以像这样创建一个队列:
(注意:此代码实际上是针对堆栈,而不是队列。请参阅下面的注释)
@interface NSMutableArray (QueueAdditions)
- (id)pop;
- (void)push:(id)obj;
@end
@implementation NSMutableArray (QueueAdditions)
- (id)pop
{
// nil if [self count] == 0
id lastObject = [[[self lastObject] retain] autorelease];
if (lastObject)
[self removeLastObject];
return lastObject;
}
- (void)push:(id)obj
{
[self addObject: obj];
}
@end
没有真正的队列集合类,但 NSMutableArray 可以有效地用于同样的事情。如果需要,您可以定义一个类别以添加弹出/推送方法以方便使用。
是的,使用 NSMutableArray。NSMutableArray 实际上实现为 2-3 树;您通常不需要关心在任意索引处从 NSMutableArray 添加或删除对象的性能特征。
re:Wolfcow -- 这是 Wolfcow 的 dequeue 方法的更正实现
- (id)dequeue {
if ([self count] == 0) {
return nil;
}
id queueObject = [[[self objectAtIndex:0] retain] autorelease];
[self removeObjectAtIndex:0];
return queueObject;
}
使用类别的解决方案NSMutableArray
不是真正的队列,因为NSMutableArray
公开了作为队列超集的操作。例如,您不应该被允许从队列中间删除一个项目(因为这些类别解决方案仍然允许您这样做)。最好封装功能,这是面向对象设计的主要原则。
标准队列.h
#import <Foundation/Foundation.h>
@interface StdQueue : NSObject
@property(nonatomic, readonly) BOOL empty;
@property(nonatomic, readonly) NSUInteger size;
@property(nonatomic, readonly) id front;
@property(nonatomic, readonly) id back;
- (void)enqueue:(id)object;
- (id)dequeue;
@end
标准队列.m
#import "StdQueue.h"
@interface StdQueue ()
@property(nonatomic, strong) NSMutableArray* storage;
@end
@implementation StdQueue
#pragma mark NSObject
- (id)init
{
if (self = [super init]) {
_storage = [NSMutableArray array];
}
return self;
}
#pragma mark StdQueue
- (BOOL)empty
{
return self.storage.count == 0;
}
- (NSUInteger)size
{
return self.storage.count;
}
- (id)front
{
return self.storage.firstObject;
}
- (id)back
{
return self.storage.lastObject;
}
- (void)enqueue:(id)object
{
[self.storage addObject:object];
}
- (id)dequeue
{
id firstObject = nil;
if (!self.empty) {
firstObject = self.storage.firstObject;
[self.storage removeObjectAtIndex:0];
}
return firstObject;
}
@end
这是我的实现,希望对您有所帮助。
有点简约,因此您必须通过在弹出时保存新磁头并丢弃旧磁头来跟踪磁头
@interface Queue : NSObject {
id _data;
Queue *tail;
}
-(id) initWithData:(id) data;
-(id) getData;
-(Queue*) pop;
-(void) push:(id) data;
@end
#import "Queue.h"
@implementation Queue
-(id) initWithData:(id) data {
if (self=[super init]) {
_data = data;
[_data retain];
}
return self;
}
-(id) getData {
return _data;
}
-(Queue*) pop {
return tail;
}
-(void) push:(id) data{
if (tail) {
[tail push:data];
} else {
tail = [[Queue alloc]initWithData:data];
}
}
-(void) dealloc {
if (_data) {
[_data release];
}
[super release];
}
@end
有什么特殊原因不能只使用 STL 队列吗?Objective C++ 是 C++ 的超集(只需使用 .mm 作为扩展名而不是 .m 以使用 Objective C++ 而不是 Objective C)。然后您可以使用 STL 或任何其他 C++ 代码。
将 STL 队列/向量/列表等与 Objective C 对象一起使用的一个问题是它们通常不支持保留/释放/自动释放内存管理。这可以通过 C++ 智能指针容器类轻松解决,该容器类在构造时保留其 Objective C 对象,并在销毁时释放它。根据您在 STL 队列中放入的内容,这通常不是必需的。
使用 NSMutableArray。