CKLinkedList
is the implementation of a doubly linked list. I'd like to see comments on the memory management (I'm using ARC in the code below) and formatting + overall style.
CKList
protocol file:
//
// CKList.h
// CollectionsKit
//
// Created by Igor Rastvorov on 11/30/14.
// Copyright (c) 2014 Igor Rastvorov. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CKCollection.h"
#import "CKQueue.h"
/**
`CKList` is a formal protocol for all the adopters that represent a heterogenous list of objects.
*/
@protocol CKList <CKCollection, CKQueue>
@property(nonatomic, assign, readonly) NSUInteger size;
/// --------------------------------
/// @name Adding objects to the list
/// --------------------------------
/**
Adds object to the head of the list.
@param object An object to add to head.
*/
-(void) addObjectToHead:(id) object;
/// -----------------------------------
/// @name Getting objects from the list
/// -----------------------------------
/**
Retrieves object at the tail of the list.
@throws `NSRangeException` if the list is empty.
*/
-(id) objectAtTail;
/**
Retrieves object at the specified position in the list.
@param index Specifies the position to remove object from.
@throws `NSRangeException` if the list is empty.
*/
-(id) objectAtIndex:(NSUInteger) index;
/// ------------------------------------
/// @name Removing objects from the list
/// ------------------------------------
/**
Removes object from the head.
@throws `NSRangeException` if the list is empty.
*/
-(void) removeObjectFromHead;
/**
Removes object from the tail.
@throws `NSRangeException` if the list is empty.
*/
-(void) removeObjectFromTail;
/**
Removes object from the specified position in the list.
@param index Specifies the position to remove object from.
@throws `NSRangeException` if the list is empty.
*/
-(void) removeObjectAtIndex:(NSUInteger) index;
/**
Constructs a new list within a range of the original list.
@param range Range of objects within the original list. Objects within the specified range will be added to a sublist.
*/
-(id <CKList>) sublistWithRange:(NSRange) range;
/**
Finds and returns an index of the first occurence of the specified object.
@param object Object first occurence of which is to be found.
*/
-(NSUInteger) indexOfObject:(id) object;
/**
Finds and returns an index of the last occurence of the specified object.
@param object Object last occurence of which is to be found.
*/
-(NSUInteger) lastIndexOfObject:(id) object;
@end
CKLinkedList.h file:
//
// CKLinkedList.h
// CKLinkedList
//
// Created by Igor Rastvorov on 11/27/14.
// Copyright (c) 2014 Igor Rastvorov. All rights reserved.
// ARC compatibe.
#import <Foundation/Foundation.h>
#include "CKList.h"
@class CKListNode;
/**
Represents a linked list of objects.
*/
@interface CKLinkedList : NSObject <CKList> {
CKListNode *_head;
CKListNode *_tail;
}
@end
CKLinkedList.m file:
//
// CKLinkedList.m
// CKLinkedList
//
// Created by Igor Rastvorov on 11/27/14.
// Copyright (c) 2014 Igor Rastvorov. All rights reserved.
#import "CKLinkedList.h"
/**
Represents a single node in a doubly linked list.
*/
@interface CKListNode : NSObject
@property(nonatomic, strong) CKListNode *next;
@property(nonatomic, strong) CKListNode *previous;
@property(nonatomic, strong) id data;
-(id) initWithData:(id) data;
-(BOOL) isEqual:(id)object;
@end
@implementation CKListNode
@synthesize next = _next;
@synthesize data = _data;
-(id) initWithData:(id)data {
self = [super init];
if (self) {
[self setData:data];
}
return self;
}
-(BOOL) isEqual:(id)object {
if (object != nil && [object isKindOfClass:[CKListNode class]]) {
return [[self data] isEqual:[object data]];
}
return NO;
}
-(NSString *) description {
return [[self data] description];
}
@end
@interface CKLinkedList ()
@property(nonatomic, readwrite) NSUInteger size;
-(CKListNode *) listNodeAtIndex:(NSUInteger) index;
-(void) removeNodeAtIndex:(NSUInteger) index;
@end
@implementation CKLinkedList
@synthesize size = _size;
#pragma mark - Initializing
-(instancetype) init {
return [self initWithArray: nil];
}
-(id) initWithArray:(NSArray *)array {
self = [super init];
if (self) {
if (array != nil) {
for (id object in array) {
[self addObjectToTail:object];
}
}
}
return self;
}
#pragma mark - Adding objects
-(void) addObjectToHead:(id)object {
CKListNode *listNode = [[CKListNode alloc] initWithData:object];
listNode.data = object;
listNode.previous = nil;
listNode.next = _head;
_head.previous = listNode;
_head = listNode;
if ([self isEmpty]) {
_tail = _head;
}
++self.size;
}
#pragma mark - CKQueue
-(void) addObjectToTail:(id)object {
CKListNode *listNode = [[CKListNode alloc] initWithData:object];
if ([self isEmpty]) {
_head = listNode;
_tail = _head;
listNode.previous = nil;
} else {
listNode.previous = _tail;
}
listNode.next = nil;
listNode.data = object;
_tail.next = listNode;
_tail = listNode;
++self.size;
}
#pragma mark - Retrieving objects
-(id) objectAtTail {
return [_tail data];
}
-(id) objectAtIndex:(NSUInteger) index {
return [[self listNodeAtIndex:index] data];
}
-(id <CKList>) sublistWithRange:(NSRange) range {
id <CKList> sublist = [[CKLinkedList alloc] init];
NSUInteger endIndex = range.location + range.length;
for (NSUInteger startIndex = range.location; startIndex <= endIndex; ++startIndex) {
CKListNode *currentNode = [self listNodeAtIndex:startIndex];
[sublist addObjectToTail: currentNode];
}
return sublist;
}
-(NSUInteger) indexOfObject:(id) object {
NSUInteger size = [self size];
for (NSUInteger index = 0; index <= size; ++index) {
if ([[self objectAtIndex:index] isEqual:object]) {
return index;
}
}
return NSNotFound;
}
-(NSUInteger) lastIndexOfObject:(id) object {
for (NSUInteger index = [self size] - 1; index != 0; --index) {
if ([[self objectAtIndex:index] isEqual:object]) {
return index;
}
}
return NSNotFound;
}
#pragma mark - CKQueue
-(id) objectAtHead {
return [_head data];
}
#pragma mark - Removing objects
-(void) removeObjectFromTail {
[self removeObjectAtIndex: [self size] - 1];
}
-(void) removeObjectAtIndex:(NSUInteger) index {
[self removeNodeAtIndex:index];
--self.size;
}
#pragma mark - CKQueue
-(void) removeObjectFromHead {
[self removeObjectAtIndex:0];
}
#pragma mark - CKCollection
-(void) clear {
for (NSUInteger listNodeIndex = 0; listNodeIndex < [self size]; ++listNodeIndex) {
[self removeObjectFromHead];
}
}
#pragma mark - List state
#pragma mark - CKCollection
-(BOOL) containsObject:(id) object {
NSUInteger size = [self size];
for (NSUInteger i = 0; i < size; ++i) {
if ([[self objectAtIndex:i] isEqual:object]) {
return YES;
}
}
return NO;
}
-(NSString *) description {
if ([self isEmpty]) {
return @"(empty)";
}
NSString *contents = [NSMutableString stringWithString:@"(\n"];
NSUInteger listNodeIndex;
for (listNodeIndex = 0; listNodeIndex < [self size] - 1; ++listNodeIndex) {
contents = [contents stringByAppendingString:[NSString stringWithFormat:@"\t%@,\n", [self listNodeAtIndex:listNodeIndex]]];
}
contents = [contents stringByAppendingString:[NSString stringWithFormat:@"\t%@\n)", [self listNodeAtIndex:listNodeIndex]]];
return contents;
}
-(BOOL) isEmpty {
return ([self size] == 0);
}
-(BOOL) isEqual:(id) object {
if (object != nil && [object isKindOfClass:[CKLinkedList class]]) {
if ([self size] != [object size]) {
return NO;
}
CKListNode *listNode = nil;
CKListNode *comparedListNode = nil;
for (NSUInteger listNodeIndex = 0; listNodeIndex < [self size]; ++listNodeIndex) {
listNode = [self listNodeAtIndex:listNodeIndex];
comparedListNode = [object listNodeAtIndex:listNodeIndex];
if (![listNode isEqual:comparedListNode]) {
return NO;
}
}
}
return YES;
}
-(NSUInteger) hash {
if ([self isEmpty]) {
return [super hash];
}
NSUInteger hashCode = [[self objectAtIndex:0] hash];;
for (NSUInteger i = 1; i < [self size]; ++i) {
hashCode ^= [[self objectAtIndex:i] hash];
}
return hashCode;
}
-(NSArray *) toArray {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self size]];
for (NSUInteger index = 0; index < [self size]; ++index) {
[array addObject:[self objectAtIndex:index]];
}
return array;
}
// ---------------------------------
// Private interface
// ---------------------------------
-(CKListNode *) listNodeAtIndex:(NSUInteger) index {
if (index >= [self size] ) {
return nil;
}
CKListNode *listNode = _head;
for (NSUInteger listNodeIndex = 0; listNodeIndex < index; ++listNodeIndex) {
listNode = [listNode next];
}
return listNode;
}
-(void) removeNodeAtIndex:(NSUInteger) index {
CKListNode *deallocationTarget = [self listNodeAtIndex:index];
if (deallocationTarget == nil) {
return;
}
if (deallocationTarget == _head) {
_head = deallocationTarget.next;
} else if (deallocationTarget == _tail) {
_tail = deallocationTarget.previous;
} else {
deallocationTarget.previous.next = deallocationTarget.next;
deallocationTarget.next.previous = deallocationTarget.previous;
}
deallocationTarget = nil;
}
@end
For reference, here is the code on GitHub.