如何在Objective-C中创建和使用队列?


107

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

Answers:


153

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

祝你好运,继续编码!


1
对于那些希望返回nil而不是在尝试从空队列中出队时引发异常的人,我在出队开始时添加了一条注释行。IMO遵循NSMutableArray引发异常的行为与Cocoa更加一致。毕竟,您可以-count事先致电以检查是否有任何对象要出队。确实,这是一个偏好问题。
Quinn Taylor

2
我已将此代码添加到github存储库中。随意分叉或让我知道我是否遇到了问题: github.com/esromneb/ios-queue-object 谢谢!!!
portforwardpodcast

2
我是否缺少某些东西,或者此实现在出队时是否具有O(n)复杂性?这太可怕了。使用圆形数组实现会更好。此实现可能有效,但是O(n)出队的想法很痛苦。
ThatGuy 2014年

11
@Wolfcow,当您从索引0中删除一个对象时,数组中的每个对象都向下移动了一个。因此,要删除单个项目,它为O(n)。对于小型队列来说可能很好,这可能是移动应用程序中99%的时间,但这对于在时间紧迫的情况下的大型数据集来说是一个糟糕的解决方案。同样,在大多数客观C情况下您也不会发现这一点。
ThatGuy 2014年

2
@ThatGuy有点晚了,但是NSArray是用循环缓冲区实现的,因此运行时不会是theta(N)。
hhanesand

33

我不会说使用NSMutableArray一定是最好的解决方案,尤其是在添加带有类别的方法时,由于方法名称冲突可能导致它们的脆弱性,因此尤其如此。对于快速脏的队列,我将使用方法在可变数组的末尾添加和删除。但是,如果您打算重用队列,或者希望代码更具可读性和自明性,则可能需要专用的队列类。

Cocoa没有内置选项,但是还有其他选择,您也不必从头开始编写一个。对于只在末端添加和删除的真正队列,循环缓冲区数组是一种非常快速的实现。查看CHDataStructures.framework,这是我一直在研究的Objective-C库/框架。它具有队列的各种实现,以及堆栈,双端队列,排序集等。就您的目的而言,与使用NSMutableArray相比,CHCircularBufferQueue显着更快(即可以用基准进行验证)和可读性(主观上是主观的)。

使用本机的Objective-C类而不是C ++ STL类的一大优点是,它与Cocoa代码无缝集成,并且在编码/解码(序列化)方面工作得更好。它也与垃圾回收和快速枚举(两者均存在于10.5+中,但仅在iPhone上具有后者)完美配合,您不必担心什么是Objective-C对象以及什么是C ++对象。

最后,尽管在任一端添加和删除NSMutableArray都比标准C数组更好,但它也不是队列的最快解决方案。对于大多数应用程序来说,这是令人满意的,但是如果需要速度,则循环缓冲区(或在某些情况下,为使高速缓存行保持高温而优化的链表)可以轻易地使NSMutableArray中断。


2
很高兴有人真正回答了一个真正的队列解决方案
Casebash

所有链接都断开了-我在哪里可以得到那个框架?我已经阅读了很多有关它的好东西,但是找不到实际的代码!
amok 2010年

该框架听起来很有希望,但是到SVN的链接仍然中断。有机会在某处获取代码吗?编辑:得到它从mac.softpedia.com/progDownload/...,但我看不出这是否是最新版本

戴夫·德隆的吉特 回购克隆现在看来是热门回购。
Regexident 2014年

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

7
您是否知道在这里实现了堆栈,而不是队列?
Jim Puls

啊-对不起!-请参阅下面的Wolfcow修改。
Ben Gotow 09年

如果您将“最佳选择”替换为“最简单的选择”,我将表示同意。:-)数据结构纯粹主义者和性能痴迷者更喜欢真正的队列,但是NSMutableArray可以轻松地代表队列。
奎因·泰勒,

3
+1 ben因为我想要一个堆栈解决方案,即使请求了队列:)
whitneyland 2011年

我所能想到的就是痛苦。您要在数组的开头插入对象,然后每次插入时都需要在1个空间中复制每个元素。在这种情况下,链表的性能会更好。
TheM00s3

8

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


的确,尽管从前端删除并不是数组结构擅长的事情,但NSMutableArray却构成了一个相当不错的队列。即便如此,对于较小的队列,性能也不是主要问题。我的一个朋友的博客上讲述这个话题而回...... sg80bab.blogspot.com/2008/05/...
奎因·泰勒

7

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


1
NSArray(和扩展名为NSMutableArray)是一个类集群,这意味着它具有几个私有实现,可以在后台互换使用。通常会得到一个取决于元素的数量。此外,Apple可以随时随时更改任何给定实现的细节。但是,您是正确的,它通常比标准阵列灵活得多。
奎因·泰勒,

5

re:Wolfcow-这是Wolfcow出队方法的正确实现

- (id)dequeue {
    if ([self count] == 0) {
        return nil;
    }
    id queueObject = [[[self objectAtIndex:0] retain] autorelease];
    [self removeObjectAtIndex:0];
    return queueObject;
}

4

在使用类别的解决方案NSMutableArray不是真正的队列,因为NSMutableArray公开的操作是队列的超集。例如,不应允许您从队列中间删除项目(因为这些类别解决方案仍然可以让您执行此操作)。最好封装功能,这是面向对象设计的主要原理。

标准队列

#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

标准队列

#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

有人可能会争辩说,使用某些技术(即KVC)可以直接访问和操作内部存储阵列,但比使用类别要好得多。
vikingosegundo 2014年

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

2

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

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


1
这似乎并不是一个好主意……只是因为您可以做某事并不意味着您应该这样做。仅出于队列类的目的引入整个STL和C ++生态系统绝对是过大的选择。
extropic-engine

3
实际上,自从发布以来,这已经成为一个更好的主意。目标C ++ / ARC意味着您可以将STL容器与目标C对象指针一起使用,并且一切都可以正常工作。ARC在C ++结构中自动为您管理内存管理。我通常也会认为C ++是更好的C语言,它使Objective-C ++通常比普通的Objective C更好的选择(例如,提供诸如enum类之类的东西)。我非常怀疑添加STL / C ++对任何现实应用程序的大小都会产生明显影响。
彼得·刘易斯

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.