此项目的目标是实现 Objective-C 的 持久数据结构。目前有 Vector、Set 和 HashMap。
使用 CocoaPods,如同添加以下内容到 Podfile
。
pod 'Persistent'
。
目前是 Clojure 的 PersistentVector 和 Dart 的 Persistent Vector 的移植。它是一个持久的位分区向量 trie,它使用了与 Clojure 和 Dart 相同的优化 - 尾部和 transient。
基本操作包括 addObject:
/removeLastObject
和 objectAtIndex:
/replaceObjectAtIndex:withObject:
,还有一个 withTransient
方法,允许您在操作向量时临时将它变为可变的,避免每次操作都创建实例。
它没有在向量中间插入/删除元素的方法。不幸的是,您必须以较慢的方式这样做,即从头创建一个新向量(对于删除则不包括该元素)。
#import "AAPersistent.h"
/// Initialization
AAPersistentVector *vector1 = [[AAPersistentVector alloc] init];
// But if you need a persistent empty vector, better use the pre-created empty one.
AAPersistentVector *vector2 = [AAPersistentVector empty];
// Or you can load an array into it during initialization:
AAPersistentVector *vector = [[AAPersistentVector alloc] initWithArray:@[@1, @2, @3]];
/// CRUD operations
[vector objectAtIndex:1]; // => @2
vector = [vector addObject:@4]; // => vector with @1, @2, @3, @4
vector = [vector replaceObjectAtIndex:2 withObject:@5]; // => vector with @1, @2, @5, @4
vector = [vector removeLastObject]; // => vector with @1, @2, @5
/// Convert to NSArray
[vector toArray]; // => @[@1, @2, @5]
/// Iterating
// With Fast Enumeration
for (id value in vector) {
// going to iterate through values with `value` = @1, @2 and @5
}
// With Iterator
id<AAIIterator> iterator = [vector iterator];
while (iterator) {
[iterator first]; // => @1, @2 and @5
iterator = [iterator next];
}
// With each (fastest way)
[vector each:^(id value) {
// going to iterate through values with `value` = @1, @2 and @5
}];
/// Transients
// Efficiently add 100 objects to the vector. Will be ~10 times faster than
// adding without transient
[vector withTransient:^(AATransientVector *transient) {
for (NSUInteger i = 0; i < 100; i += 1) {
transient = [transient addObject:@(i)];
}
return transient;
}];
// You can also do that without a block, if you want
AATransientVector *transientVector = [vector asTransient];
for (NSUInteger i = 0; i < 100; i += 1) {
transientVector = [transientVector addObject:@(i)];
}
vector = [transientVector asPersistent];
它也是 Clojure 的 PersistentHashMap 的移植。它是一个基于 Hash Array Mapped Trie 的数据结构,只不过它是持久的并有一些优化(transients) - 与 Clojure 的哈希表相同的优化。
主要有 objectForKey:
、setObject:forKey
和 removeObjectForKey:
方法,允许您向、修改、读取和从映射中删除键和值。它也有 withTransient
方法,允许更快地进行批量操作。
#import "AAPersistent.h"
/// Initialization
AAPersistentHashMap *map1 = [[AAPersistentHashMap alloc] init];
// But if you need a persistent empty map, better use the pre-created empty one.
AAPersistentHashMap *map2 = [AAPersistentHashMap empty];
// Or you can load a dictionary into it during initialization:
AAPersistentHashMap *map = [[AAPersistentHashMap alloc]
initWithDictionary:@{@1: @2, @3: @4}
];
/// CRUD operations
[map objectForKey:@1]; // => @2
map = [map setObject:@6 forKey:@5]; // => map with @1: @2, @3: @4, @5: @6
map = [map removeObjectForKey:@3]; // => map with @1: @2, @5: @6
/// Convert to NSDictionary
[map toDictionary]; // => @{@1: @2, @5: @6}
/// Iterating
// With Fast Enumeration
for (id value in map) {
// going to iterate through values with `value` = @1 and @5
}
// With Iterator
id<AAIIterator> iterator = [map iterator];
while (iterator) {
[iterator first]; // => @[@1, @2] and @[@5, @6]
iterator = [iterator next];
}
// With each (fastest way)
[map each:^(id key, id value) {
// going to iterate through keys and values
}];
/// Transients
// Efficiently set 100 objects in the map. Will be ~10 times faster than
// adding without transient
[map withTransient:^(AATransientHashMap *transient) {
for (NSUInteger i = 0; i < 100; i += 1) {
transient = [transient setObject:@(i + 1) forKey:@(i)];
}
return transient;
}];
// You can also do that without a block, if you want
AATransientHashMap *transientMap = [map asTransient];
for (NSUInteger i = 0; i < 100; i += 1) {
transientMap = [transientMap setObject:@(i + 1) forKey:@(i)];
}
map = [transientMap asPersistent];
这只是一个基于 AAPersistentHashMap
的代理,在持久化哈希表上实现了集合功能。
#import "AAPersistent.h"
/// Initialization
AAPersistentSet *set1 = [[AAPersistentSet alloc] init];
// But if you need a persistent empty vector, better use the pre-created empty one.
AAPersistentSet *set2 = [AAPersistentSet empty];
// Or you can load an array into it during initialization:
AAPersistentSet *set = [[AAPersistentSet alloc]
initWithSet:[NSSet setWithObjects:@1, @2, @3, nil]
];
/// CRUD operations
[set containsObject:@1]; // => YES
set = [set addObject:@4]; // => set with @1, @2, @3, @4
set = [set addObject:@3]; // => set with @1, @2, @3, @4
set = [set removeObject:@3]; // => set with @1, @2, @4
/// Convert to NSSet
[set toSet]; // => NSSet with @1, @2 and @4
/// Iterating
// With Fast Enumeration
for (id value in set) {
// going to iterate through values with `value` = @1, @2 and @4
}
// With Iterator
id<AAIIterator> iterator = [set iterator];
while (iterator) {
[iterator first]; // => @1, @2 and @4
iterator = [iterator next];
}
// With each (fastest way)
[set each:^(id value) {
// going to iterate through values with `value` = @1, @2 and @4
}];
/// Transients
// Efficiently add 100 objects to the set. Will be ~10 times faster than
// adding without transient
[set withTransient:^(AATransientSet *transient) {
for (NSUInteger i = 0; i < 100; i += 1) {
transient = [transient addObject:@(i)];
}
return transient;
}];
// You can also do that without a block, if you want
AATransientSet *transientVector = [set asTransient];
for (NSUInteger i = 0; i < 100; i += 1) {
transientVector = [transientVector addObject:@(i)];
}
set = [transientVector asPersistent];
有两个函数,定义在 "AAPersistentFunctions.h" 中 - persist
和 unpersist
。它们可以将常规嵌套的数组/字典/集合转换为持久化向量/映射/集合,如下所示
AAPersistentHashMap *map = (AAPersistentHashMap *)persist(
@{@"foo": @"bar", @"bla": @[@"zoo", @{@"abc": @"def"}]}
);
以及恢复回来
NSDictionary *dict = (NSDictionary *)unpersist(map);
通常,你的数据结构看起来像一棵树(或 JSON 结构),包含嵌套的字典和数组。因为这些结构是不可变的,你不能简单地这样做
AAPersistentHashMap *map = (AAPersistentHashMap *)persist(
@{@"foo": @"bar", @"bla": @[@"zoo", @{@"abc": @"def"}]}
);
map[@"bla"][1][@"abc"] = @"new_value"; // Obviously won't work
所以,存在方便的方法 objectAt:
、insertAt:withValue:
、setAt:withValue
、addAt:withValue
和 removeAt:
,针对这些情况。它的工作方式如下
/// objectAt:
AAPersistentHashMap *map = (AAPersistentHashMap *)persist(
@{@"foo": @"bar", @"bla": @[@"zoo", @{@"abc": @"def"}]}
);
AAPersistentHashMap *map1;
NSString *value = [map objectAt:@[@"bla", @1, @"abc"]]; // => @"def"
/// insertAt:withValue:
map1 = [map insertAt:@[@"bla", @1, @"abc"] withValue:@"new one"];
// Map1 is @{@"foo": @"bar", @"bla": @[@"zoo", @{@"abc": @"new one"}]}
// It surely also works for vector, but very unefficient if inserted not at the last position
map1 = [map insertAt:@[@"bla", @0] withValue:@"new one"];
// Map1 is @{@"foo": @"bar", @"bla": @[@"new_one", @"zoo", @{@"abc": @"def"}]}
/// removeAt:withValue:
map2 = [map removeAt:@[@"bla", @1, @"abc"]];
// Map2 is @{@"foo": @"bar", @"bla": @[@"zoo"]}
// It surely also works for vector, but very unefficient if removed not at the last position
map1 = [map removeAt:@[@"bla", @0]];
// Map1 is @{@"foo": @"bar", @"bla": @[@{@"abc": @"def"}]}
/// setAt:withValue:
/// It's the same as insertAt:withValue for hashMap for a vector, but slightly different for a vector.
/// For vector, it replaces the value at the index instead of inserting a new one
map1 = [map setAt:@[@"bla", @0] withValue:@"new one"];
// Map1 is @{@"foo": @"bar", @"bla": @[@"new_one", @{@"abc": @"def"}]}
/// addAt:withValue:
/// It only works for vectors, just adds a value to the end
map1 = [map addAt:@[@"bla"] withValue:@"new one"];
// Map1 is @{@"foo": @"bar", @"bla": @[@"zoo", @{@"abc": @"def"}, @"new one"]}
以一种相对简单的方式进行了测试 - 使用 1000000 条记录进行了以下操作,并比较了结果。在可能的情况下使用了瞬态数据结构。我使用我的 MacbookPro i7 2.7GHz 16GB RAM 进行了测试。
比 NSMutableDictionary 慢
each:
)的性能大约是 ~15x (1.243s / 0.074s)比 NSMutableArray 慢
each:
),性能大约是 ~120x (5.24s / 0.042s)这些数据结构实际上并不适用于生产,迄今为止还没有经过彻底测试。但你可以尝试使用它,如果找到任何错误或希望添加任何额外功能都请告诉我 :) 欢迎Pull Requests。