107

我想在我的 Objective-C 程序中使用队列数据结构。在 C++ 中,我会使用 STL 队列。Objective-C 中的等效数据结构是什么?如何推送/弹出项目?

4

10 回答 10

155

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 方法一样调用它们。

祝你好运,继续编码!

于 2009-06-01T20:03:30.247 回答
33

我不会说使用 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。

于 2009-06-10T05:06:20.337 回答
29

据我所知,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
于 2009-05-03T17:07:08.177 回答
8

没有真正的队列集合类,但 NSMutableArray 可以有效地用于同样的事情。如果需要,您可以定义一个类别以添加弹出/推送方法以方便使用。

于 2009-05-03T17:03:04.893 回答
7

是的,使用 NSMutableArray。NSMutableArray 实际上实现为 2-3 树;您通常不需要关心在任意索引处从 NSMutableArray 添加或删除对象的性能特征。

于 2009-05-06T06:48:50.353 回答
5

re:Wolfcow -- 这是 Wolfcow 的 dequeue 方法的更正实现

- (id)dequeue {
    if ([self count] == 0) {
        return nil;
    }
    id queueObject = [[[self objectAtIndex:0] retain] autorelease];
    [self removeObjectAtIndex:0];
    return queueObject;
}
于 2009-11-10T00:49:22.130 回答
4

使用类别的解决方案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
于 2014-07-07T23:59:15.130 回答
3

这是我的实现,希望对您有所帮助。

有点简约,因此您必须通过在弹出时保存新磁头并丢弃旧磁头来跟踪磁头

@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
于 2012-08-30T21:22:18.537 回答
2

有什么特殊原因不能只使用 STL 队列吗?Objective C++ 是 C++ 的超集(只需使用 .mm 作为扩展名而不是 .m 以使用 Objective C++ 而不是 Objective C)。然后您可以使用 STL 或任何其他 C++ 代码。

将 STL 队列/向量/列表等与 Objective C 对象一起使用的一个问题是它们通常不支持保留/释放/自动释放内存管理。这可以通过 C++ 智能指针容器类轻松解决,该容器类在构造时保留其 Objective C 对象,并在销毁时释放它。根据您在 STL 队列中放入的内容,这通常不是必需的。

于 2009-06-02T09:28:57.350 回答
1

使用 NSMutableArray。

于 2009-05-03T17:04:30.820 回答