Objective-Cでキューを作成して使用するにはどうすればよいですか?


107

Objective-Cプログラムでキューデータ構造を使用したい。C ++では、STLキューを使用します。Objective-Cの同等のデータ構造は何ですか?アイテムをプッシュ/ポップするにはどうすればよいですか?

回答:


153

ベンのバージョンはキューではなくスタックなので、少し調整しました:

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、2011年

2
私は何か不足していますか、またはこの実装はデキュー時にO(n)の複雑さを持っていますか?それはひどい。あなたは循環配列の実装ではるかに良くなるでしょう。この実装は機能するかもしれませんが、O(n)デキューのアイデアは苦痛です。
ThatGuy 2014

11
@Wolfcow、インデックス0からオブジェクトを削除すると、配列内のすべてのオブジェクトが1つ下にシフトされます。したがって、単一のアイテムを削除するには、O(n)です。おそらく、モバイルアプリケーションでは99%の時間である小さなキューにはおそらく問題ありませんが、これは、時間が重要な状況での大規模なデータセットの場合、ひどい解決策になります。繰り返しになりますが、ほとんどの客観的なCの状況ではそうはいきません。
ThatGuy

2
@ThatGuy少し遅れますが、NSArrayは循環バッファで実装されているため、ランタイムはtheta(N)にはなりません。
hhanesand 2018年

33

NSMutableArrayを使用することが必ずしも最善の解決策であるとは言えません。特に、メソッドにカテゴリを追加する場合は、メソッド名が衝突した場合に脆弱性が生じる可能性があるためです。quick-n-dirtyキューの場合は、メソッドを使用して、可変配列の最後に追加および削除します。ただし、キューの再利用を計画している場合、またはコードをより読みやすく自明にしたい場合は、専用のキュークラスがおそらく必要です。

Cocoaには組み込みのものはありませんが、他のオプションがあり、最初から作成する必要もありません。最後からのみ追加および削除する真のキューの場合、循環バッファー配列は非常に高速な実装です。私が取り組んできたObjective-Cのライブラリ/フレームワークであるCHDataStructures.frameworkを確認してください。キュー、スタック、デキュー、ソート済みセットなどのさまざまな実装があります。目的のために、CHCircularBufferQueueは、NSMutableArrayを使用するよりもはるかに高速(つまり、ベンチマークで証明可能)であり、より読みやすく(確かに主観的)です。

C ++ STLクラスの代わりにネイティブのObjective-Cクラスを使用する大きな利点の1つは、Cocoaコードとシームレスに統合され、エンコード/デコード(シリアル化)でより適切に機能することです。また、ガベージコレクションと高速列挙(両方とも10.5以降に存在しますが、iPhoneでは後者のみ)と完全に連携し、Objective-CオブジェクトとC ++オブジェクトについて心配する必要はありません。

最後に、NSMutableArrayは、両端から追加および削除するときに標準のC配列よりも優れていますが、キューの最速のソリューションでもありません。ほとんどのアプリケーションでは問題ありませんが、速度が必要な場合、循環バッファー(または場合によっては、キャッシュラインをホットに保つように最適化されたリンクリスト)でNSMutableArrayを簡単に切り捨てることができます。


2
誰かが本当のキューソリューションで実際に返信して
くれてうれしい

すべてのリンクが壊れています-そのフレームワークはどこで入手できますか?私はそれについてたくさんの良いものを読みましたが、実際のコードを見つけることができません!
amok

フレームワークは有望に聞こえますが、SVNへのリンクはまだ壊れています。どこかでコードを入手するチャンスはありますか?編集:mac.softpedia.com/progDownload/から取得しましたが、これが現在のバージョンかどうかはわかりません
ケイ

Dave DeLongのGit レポクローンは、最近の主力レポのようです。
Regexident 2014年

29

私の知る限り、Objective-CはQueueデータ構造を提供していません。あなたの最善の策は、作成することでNSMutableArray、その後、使用[array lastObject][array removeLastObject]アイテムを取得するために、と[array insertObject:o atIndex:0]...

これを頻繁に行う場合は、NSMutableArrayクラスの機能を拡張するためにObjective-Cカテゴリを作成することをお勧めします。カテゴリを使用すると、既存のクラスに関数を動的に追加できます(ソースがなくても)-次のようなキューを作成できます。

(注:このコードは実際にはキューではなくスタック用です。以下のコメントを参照してください)

@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
キューではなく、ここにスタックを実装したことを知っていますか?
ジムプルス2009年

ああ-ごめんなさい!-以下のWolfcowの変更を参照してください。
ベン・ゴトウ2009年

「おすすめコンテンツ」を「最も簡単なオプション」に置き換えれば同意します。:-)データ構造の純粋主義者およびパフォーマンスの監視者は真のキューを好むでしょうが、NSMutableArrayはキューを簡単に代行できます。
クインテイラー、

3
キューが要求されたにもかかわらずスタックソリューションが欲しかったので+1をベン:)
ホイットニーランド

私が考えることができるすべては痛みです。配列の先頭にオブジェクトを挿入している場合は、挿入するたびに1つのスペースにすべての要素をコピーする必要があります。この場合、リンクリストの方がはるかに優れたパフォーマンスを発揮します。
TheM00s3 2015

8

実際のキューコレクションクラスはありませんが、NSMutableArrayを使用して同じことを効果的に行うことができます。必要に応じて、カテゴリを定義してポップ/プッシュメソッドを追加できます。


確かに、NSMutableArrayはかなりまともなキューを作成しますが、前面から削除することは配列構造が優れているものではありません。それでも、小さなキューの場合、パフォーマンスはとにかく大きな問題ではありません。私の友人は、このトピックのAについてブログバック...しばらくsg80bab.blogspot.com/2008/05/...
クインテイラー

7

はい、NSMutableArrayを使用します。NSMutableArrayは実際には2-3ツリーとして実装されます。通常は、任意のインデックスでNSMutableArrayにオブジェクトを追加または削除するパフォーマンス特性を気にする必要はありません。


1
NSArray(および拡張機能によるNSMutableArray)はクラスクラスターです。つまり、舞台裏で交換可能に使用できるプライベート実装がいくつかあります。あなたが得るものは通常、要素の数に依存します。また、Appleは、特定の実装の詳細をいつでも自由に変更できます。ただし、通常は標準の配列よりもはるかに柔軟であることは間違いありません。
クインテイラー

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;
}

4

上のカテゴリを使用するソリューションNSMutableArrayは真のキューではありません。NSMutableArrayは、キューのスーパーセットである操作を公開する。たとえば、キュ​​ーの途中からアイテムを削除することは許可されません(これらのカテゴリソリューションでは引き続き実行できるため)。オブジェクト指向設計の主要な原則である機能をカプセル化するのが最善です。

StdQueue.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

StdQueue.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

特定の手法(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 ++のスーパーセットです(Objective CではなくObjective C ++を使用するには、拡張子として.mの代わりに.mmを使用するだけです)。その後、STLまたはその他のC ++コードを使用できます。

Objective CオブジェクトでSTLキュー/ベクター/リストなどを使用する場合の問題の1つは、通常、保持/解放/自動解放のメモリ管理をサポートしないことです。これは、構築時にObjective Cオブジェクトを保持し、破棄時に解放するC ++スマートポインターコンテナークラスで簡単に回避できます。STLキューに何を入れるかによって、これは多くの場合必要ありません。


1
これは本当に良い考えのようには見えません... 何かを実行できるからといって、そうする必要があるわけではありません。キュークラスのためだけにSTLおよびC ++エコシステム全体を取り込むのは、間違いなくやりすぎです。
外向性エンジン

3
実際、それが投稿されて以来、これははるかに優れたアイデアになっています。Objective C ++ / ARCは、Objective CオブジェクトポインターでSTLコンテナーを使用でき、すべてが機能することを意味します。ARCは、C ++構造内のメモリ管理を自動的に処理します。また、C ++ははるかに優れたCであり、Objective-C ++が一般的なObjective Cよりも一般的に優れた選択肢になると一般的に主張します(たとえば、列挙型クラスなどを与える)。また、STL / C ++を追加すると、実際のアプリのサイズに大きな影響があることは間違いありません。
Peter N Lewis

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.