美文网首页
1. IOS 字典研究

1. IOS 字典研究

作者: LeeDev | 来源:发表于2019-08-02 13:23 被阅读0次

    一: Hash 算法

    1. hash算法

    hash称为散列表,其实本质上还是一个数组。主要是通过 hash(key) 生成一个index 去从数组中取出值。但是如果产生hash(key1) == hash(key2) 就是hash冲突。

    2. 冲突解决

    为了解决冲突,有 开放寻址法(open addressing)和 链表法(chaining)

    链表法(chaining)

    这个比较简单,类似一个二维数组,就是一个一维数组,存储 链表 的表头,链表根据 hash函数,记录key,并一个一个链接下去。 这里就需要记录指针,导致了相对来说存储空间的增加。

    如图,假设hash函数是对6 取余数,那么{0,6,7,2} 结果如下图:


    image

    开放寻址法(open addressing)

    开放寻址方法,比如我们一般的hash方法, p = Hash(key),假设得到的p值有冲突,那么就,p = Hash(p + di),di 称为增量序列取值为【1,2,....】 ; 而且性能依赖于装填因子 α=N/M,α 超过0.72的时候,会严重影响性能。

    1.线性探测

    很容易产生数据集中的问题

     //对m 取余数的,m一般是hash表的长度
     Hash(key) {
        return key%m 
     }
     // 获取index值
     p = Hash(key) 
     // 如果冲突 了,就 +di 往下探测,直到不冲突
     p = Hash(Hash(key)+1)
     p = Hash(Hash(key)+2)
     p = Hash(Hash(key)+3)
     p = Hash(Hash(key)+4)
     ...
    
    2.二次探测

    为了让数据分布均匀一点,让数据更加离散,就出现了二次探测,p=Hash(Hash(key)+di) di取值可能为1,-1,4,-4,9,-9,16,-16,...k2,-k2(k<=m/2)其中m为表长,di为增量序列

     // 如果冲突 了,就 +/- di^2 往下探测,直到不冲突
     p = Hash(Hash(key)+1)
     p = Hash(Hash(key)-1)
     p = Hash(Hash(key)+4)
     p = Hash(Hash(key)-4)
     ...
    

    二: NSDictory内部实现

    NSDictionary是可以通过CFDictionary toll-free bridged 过来的。而且我们是可以查看CFDictionary.

    用CFDictionary研究和使用

    1. 首先我们查看下一下,用CFDictionary 创建

    CFStringRef key1 = CFStringCreateWithCString(CFAllocatorGetDefault(), "name", kCFStringEncodingUTF8);
        CFStringRef key2 = CFStringCreateWithCString(CFAllocatorGetDefault(), "age", kCFStringEncodingUTF8);
        CFStringRef keys[] = {key1, key2};
        
        CFStringRef values[] = {CFSTR("leeDev"), CFSTR("27")};
        int count = sizeof(values)/sizeof(values[0]);
        CFDictionaryRef dic = CFDictionaryCreate(CFAllocatorGetDefault(), (const void**)keys, (const void**)values,count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        
        NSLog(@"count = %ld,value = %@",(long)CFDictionaryGetCount(dic),CFDictionaryGetValue(dic, key1));
        
        // 可变字典
        CFMutableDictionaryRef mutDic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        CFDictionaryAddValue(mutDic, keys[0], values[0]);
        CFDictionarySetValue(mutDic, keys[0], CFSTR("lisi"));
        NSLog(@"mutDic = %@",mutDic);
    

    我们找到 CFDictionaryCreate 中 方法,传入4个值

    • keys
    • values
    • count
    • kCFTypeDictionaryKeyCallBacks:
    • kCFTypeDictionaryValueCallBacks

    2. 我们通过查看CF-855.17中的创建源码,并删除一些异常判断的代码

    CFHashRef CFDictionaryCreate(CFAllocatorRef allocator, const_any_pointer_t *klist, const_any_pointer_t *vlist, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) {
       
        CFTypeID typeID = CFDictionaryGetTypeID();
        
        //1.创建hash表
        CFBasicHashRef ht = __CFDictionaryCreateGeneric(allocator, keyCallBacks, valueCallBacks, CFDictionary);
        
        //2.给hash表设置容量
        if (0 < numValues) CFBasicHashSetCapacity(ht, numValues);
        
        //3. 给hash表设置 keys 和 values
        for (CFIndex idx = 0; idx < numValues; idx++) {
            CFBasicHashAddValue(ht, (uintptr_t)klist[idx], (uintptr_t)vlist[idx]);
        }
        
        //4.设置不可变的hash表
        CFBasicHashMakeImmutable(ht);
        _CFRuntimeSetInstanceTypeIDAndIsa(ht, typeID);
    
        return (CFHashRef)ht;
    }
    
    
    2.1 在给hash表的添加值的时候有个方法是 CFBasicHashAddValue
    static void __CFBasicHashAddValue(CFBasicHashRef ht, CFIndex bkt_idx, uintptr_t stack_key, uintptr_t stack_value) {
        //1.记录操作数
        ht->bits.mutations++;
        //2. 如果我们的hash表的容量 不够,需要Rehash
        if (CFBasicHashGetCapacity(ht) < ht->bits.used_buckets + 1) {
            // 2.1.1 下面会有容量数组的介绍。
            __CFBasicHashRehash(ht, 1);
            //2.1.2 就是通过 开发地址方法来找hash的index
            bkt_idx = __CFBasicHashFindBucket_NoCollision(ht, stack_key, 0);
        } else if (__CFBasicHashIsDeleted(ht, bkt_idx)) {
            ht->bits.deleted--;
        }
        //2.1.3 key_hash
        uintptr_t key_hash = 0;
        if (__CFBasicHashHasHashCache(ht)) {
            key_hash = __CFBasicHashHashKey(ht, stack_key);
        }
        // 2.1.4 通过对value就行retain操作
        stack_value = __CFBasicHashImportValue(ht, stack_value);
        
        if (ht->bits.keys_offset) {
           // 2.1.5 对key 进行retain操作
            stack_key = __CFBasicHashImportKey(ht, stack_key);
        }
        //2.1.6 hash 表里面存入的是 CFBasicHashValue的对象
        //就是把 stack_value 和 stack_key 存入到 CFBasicHashValue
        __CFBasicHashSetValue(ht, bkt_idx, stack_value, false, false);
        if (ht->bits.keys_offset) {
            __CFBasicHashSetKey(ht, bkt_idx, stack_key, false, false);
        }
        if (ht->bits.counts_offset) {
            __CFBasicHashIncSlotCount(ht, bkt_idx);
        }
        if (__CFBasicHashHasHashCache(ht)) {
            __CFBasicHashGetHashes(ht)[bkt_idx] = key_hash;
        }
        ht->bits.used_buckets++;
    }
    
    
    2.1.1 容量数组,用于字典容量的申请 ,就是说,__CFBasicHashRehash(ht, 1);执行后,假设开始的容量是0,那么Rehash后就是3; 如果是3,那么Rehash后就是6
    static const uintptr_t __CFBasicHashTableCapacities[64] = {
        0, 3, 6, 11, 19, 32, 52, 85, 118, 155, 237, 390, 672, 1065,
        1732, 2795, 4543, 7391, 12019, 19302, 31324, 50629, 81956,
        132580, 214215, 346784, 561026, 907847, 1468567, 2376414,
        3844982, 6221390, 10066379, 16287773, 26354132, 42641916,
        68996399, 111638327, 180634415, 292272755,
    #if __LP64__
        472907503UL, 765180257UL, 1238087439UL, 2003267722UL, 3241355160UL,
    #if 0
        5244622578UL, 8485977737UL, 13730600347UL, 22216578100UL,
        35947178453UL, 58163756541UL, 94110935011UL, 152274691274UL,
        246385626296UL, 398660317578UL, 645045943559UL, 1043706261135UL,
        1688752204693UL, 2732458465840UL, 4421210670552UL,
        7153669136706UL, 11574879807265UL, 18728548943682UL
    #endif
    #endif
    };
    
    2.1.2 通过开发地址 来处理,并返回当前的index
    CF_INLINE CFIndex __CFBasicHashFindBucket_NoCollision(CFConstBasicHashRef ht, uintptr_t stack_key, uintptr_t key_hash) {
        if (0 == ht->bits.num_buckets_idx) {
            return kCFNotFound;
        }
        if (ht->bits.indirect_keys) {
            switch (ht->bits.hash_style) {
            case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear_Indirect_NoCollision(ht, stack_key, key_hash);
            case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double_Indirect_NoCollision(ht, stack_key, key_hash);
            case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential_Indirect_NoCollision(ht, stack_key, key_hash);
            }
        } else {
            switch (ht->bits.hash_style) {
            case __kCFBasicHashLinearHashingValue: return ___CFBasicHashFindBucket_Linear_NoCollision(ht, stack_key, key_hash);
            case __kCFBasicHashDoubleHashingValue: return ___CFBasicHashFindBucket_Double_NoCollision(ht, stack_key, key_hash);
            case __kCFBasicHashExponentialHashingValue: return ___CFBasicHashFindBucket_Exponential_NoCollision(ht, stack_key, key_hash);
            }
        }
        HALT;
        return kCFNotFound;
    }
    
    2.2 CFBasicHashCallbacks 结构体是干嘛的?

    首先 是一个struct __CFBasicHashCallbacks 结构体,里面包含的是 函数指针,前面的 kCFTypeDictionaryKeyCallBackskCFTypeDictionaryValueCallBacks 其实主要是用来对 key 或者 value 来进行 retain等操作。

    struct __CFBasicHashCallbacks {
        uintptr_t (*retainValue)(CFAllocatorRef alloc, uintptr_t stack_value);  // Return 2nd arg or new value
        uintptr_t (*retainKey)(CFAllocatorRef alloc, uintptr_t stack_key);  // Return 2nd arg or new key
        void (*releaseValue)(CFAllocatorRef alloc, uintptr_t stack_value);
        void (*releaseKey)(CFAllocatorRef alloc, uintptr_t stack_key);
        Boolean (*equateValues)(uintptr_t coll_value1, uintptr_t stack_value2); // 1st arg is in-collection value, 2nd arg is probe parameter OR in-collection value for a second collection
        Boolean (*equateKeys)(uintptr_t coll_key1, uintptr_t stack_key2); // 1st arg is in-collection key, 2nd arg is probe parameter
        CFHashCode (*hashKey)(uintptr_t stack_key);
        uintptr_t (*getIndirectKey)(uintptr_t coll_value);  // Return key; 1st arg is in-collection value
        CFStringRef (*copyValueDescription)(uintptr_t stack_value);
        CFStringRef (*copyKeyDescription)(uintptr_t stack_key);
    };
    

    相关文章

      网友评论

          本文标题:1. IOS 字典研究

          本文链接:https://www.haomeiwen.com/subject/nzhldctx.html