Objective-Cプログラムでキューデータ構造を使用したい。C ++では、STLキューを使用します。Objective-Cの同等のデータ構造は何ですか?アイテムをプッシュ/ポップするにはどうすればよいですか?
Objective-Cプログラムでキューデータ構造を使用したい。C ++では、STLキューを使用します。Objective-Cの同等のデータ構造は何ですか?アイテムをプッシュ/ポップするにはどうすればよいですか?
回答:
ベンのバージョンはキューではなくスタックなので、少し調整しました:
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には組み込みのものはありませんが、他のオプションがあり、最初から作成する必要もありません。最後からのみ追加および削除する真のキューの場合、循環バッファー配列は非常に高速な実装です。私が取り組んできたObjective-Cのライブラリ/フレームワークであるCHDataStructures.frameworkを確認してください。キュー、スタック、デキュー、ソート済みセットなどのさまざまな実装があります。目的のために、CHCircularBufferQueueは、NSMutableArrayを使用するよりもはるかに高速(つまり、ベンチマークで証明可能)であり、より読みやすく(確かに主観的)です。
C ++ STLクラスの代わりにネイティブのObjective-Cクラスを使用する大きな利点の1つは、Cocoaコードとシームレスに統合され、エンコード/デコード(シリアル化)でより適切に機能することです。また、ガベージコレクションと高速列挙(両方とも10.5以降に存在しますが、iPhoneでは後者のみ)と完全に連携し、Objective-CオブジェクトとC ++オブジェクトについて心配する必要はありません。
最後に、NSMutableArrayは、両端から追加および削除するときに標準のC配列よりも優れていますが、キューの最速のソリューションでもありません。ほとんどのアプリケーションでは問題ありませんが、速度が必要な場合、循環バッファー(または場合によっては、キャッシュラインをホットに保つように最適化されたリンクリスト)でNSMutableArrayを簡単に切り捨てることができます。
私の知る限り、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
実際のキューコレクションクラスはありませんが、NSMutableArrayを使用して同じことを効果的に行うことができます。必要に応じて、カテゴリを定義してポップ/プッシュメソッドを追加できます。
はい、NSMutableArrayを使用します。NSMutableArrayは実際には2-3ツリーとして実装されます。通常は、任意のインデックスでNSMutableArrayにオブジェクトを追加または削除するパフォーマンス特性を気にする必要はありません。
上のカテゴリを使用するソリューション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
これは私の実装です。
一種のミニマルなので、ポップ時に新しいヘッドを保存し、古いヘッドを破棄して、ヘッドを追跡する必要があります
@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 ++のスーパーセットです(Objective CではなくObjective C ++を使用するには、拡張子として.mの代わりに.mmを使用するだけです)。その後、STLまたはその他のC ++コードを使用できます。
Objective CオブジェクトでSTLキュー/ベクター/リストなどを使用する場合の問題の1つは、通常、保持/解放/自動解放のメモリ管理をサポートしないことです。これは、構築時にObjective Cオブジェクトを保持し、破棄時に解放するC ++スマートポインターコンテナークラスで簡単に回避できます。STLキューに何を入れるかによって、これは多くの場合必要ありません。
-count
事前に呼び出して、デキューするオブジェクトがあるかどうかを確認できます。本当にそれは好みの問題です。