HashMap——HashMap中的indexFor方法

HashMap 中如何确定元素的位置

​ 众所周知,在 jdk 1.7 中,HashMap 底层是由数组 + 链表的方式实现的,那我们在使用 HashMap 的时候,是如何将我们的 key-value put 到 HashMap 中的呢

HashMap 存放原理

​ 在理解 HashMap 的存放原理前,我们先来回想一下数组,当我们想给数组中的一个元素进行赋值时,我们至少需要知道两个条件,一是数组的引用名称,二是想要被赋值的数组元素的索引,即array[i]中的arrayi

​ 我们再来看看,在 jdk 1.7 中 HashMap 的结构:
在这里插入图片描述
​ 从上图我们可以看到,HashMap 的主体是一个数组,这个数组中存储的元素是一个Entry<k, v>类型的变量,这个变量有四个属性:Object keyObject valueint hashEntry next,当两个元素经过计算后的数组下标相同时,就是所谓的发生了 Hash 碰撞,这时,就需要在数组发生 Hash 碰撞的位置构造一个链表,将发生碰撞的元素以链表的形式,存放在数组中

​ 了解了 HashMap 的 构成,我们知道 HashMap 的本质也是数组,既然我们想向数组里 put 元素,那我们必然需要知道数组的引用名称和要被 put 的位置的下标,可是 HashMap 的 put 方法只有 key 和 value 两个参数,没有 int 类型的 index,那 HashMap 是如何确定每个元素会被存放到数组的哪个位置呢?

​ 这里我们看一下 HashMap 源码中的 put 部分:

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)  
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length); 
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
            V oldValue = e.value;  
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
        }  
    }  
    addEntry(hash, key, value, i);  
    return null;  
}

​ 这里我们暂且不讨论其他部分,直接看第 8 行代码,结合下面的table[i]等变量可以看到,这行代码通过indexFor函数返回的值,正是经过计算的数组下标,也就是说,HashMap 会在 put 元素时,通过元素的 hash 值以及当前数组的长度,来确定一个下标来存放元素

​ 这时我们不妨想一想,hash 值一般都是一个十分巨大的整数,例如12345643327864322等等(都是我瞎打的),而数组的长度一定是一个十分有限的数,假设是8,我们正常想通过这两个数来获取一个0~7的正数下标要怎么做?毫无以为,用hash % table.length,这样不管 hash 是一个多大的数,我们都可以得到一个在数组索引范围内的整数,那 HashMap 的 indexFor 函数中是如何做的呢?

HashMap 的 indexFor 函数

​ 这里我们直接看 indexFor 函数的代码:

/**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        return h & (length-1);
    }

​ 代码很短,只有一行,我们可以看到,HashMap 中是通过 hash 和数组长度减一得到的结果进行一次&运算,这里我们要先清楚&运算的概念:将两个二进制数进行按位&操作时,只有两个数对应的位上都为 1,结果为 1,否则都为 0

​ 我们不妨带进来一个数算一遍,这样结果比较直接,假设我们的 h 的二进制表示是1101 0110(我瞎编的),数组的长度是8,二进制就是0000 1000,这时我们先进行length - 1的操作,得到0000 0111,这时再与 hash 进行&操作时,可以得到0000 0110,即十进制的6,而 HashMap 的容量,即数组的长度永远都是2的次方,也就是说,table.length的二进制表示永远都是一个1,其余都是0的状态,例如24次方160001 00005次方320010 0000,那也就是说明,table.length - 1得到的值永远都是前一半都是0,后一半都是1,这种结构再与 hash 进行&操作时,得到的结果就和hash % table.length一样了!

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C没有内置的HashMap数据结构,但可以使用一些技巧来实现类似的功能。一种常见的方法是使用哈希表(hash table)或关联数组(associative array)来模拟HashMap。 在C,你可以使用结构体和数组来创建自己的哈希表。下面是一个简单的示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 10 // 定义哈希节点结构体 typedef struct { char* key; int value; } HashNode; // 创建哈希表结构体 typedef struct { HashNode* nodes[SIZE]; } HashMap; // 哈希函数 int hashFunction(const char* key) { int sum = 0; for (int i = 0; i < strlen(key); i++) { sum += (int)key[i]; } return sum % SIZE; } // 初始化哈希表 void initHashMap(HashMap* hashMap) { for (int i = 0; i < SIZE; i++) { hashMap->nodes[i] = NULL; } } // 插入键值对到哈希表 void insert(HashMap* hashMap, const char* key, int value) { int index = hashFunction(key); HashNode* newNode = (HashNode*)malloc(sizeof(HashNode)); newNode->key = strdup(key); newNode->value = value; hashMap->nodes[index] = newNode; } // 根据键查找值 int get(HashMap* hashMap, const char* key) { int index = hashFunction(key); HashNode* node = hashMap->nodes[index]; if (node != NULL && strcmp(node->key, key) == 0) { return node->value; } return -1; // 找不到返回-1 } int main() { HashMap hashMap; initHashMap(&hashMap); insert(&hashMap, "apple", 5); insert(&hashMap, "banana", 3); insert(&hashMap, "orange", 7); printf("Value for 'apple': %d\n", get(&hashMap, "apple")); printf("Value for 'banana': %d\n", get(&hashMap, "banana")); printf("Value for 'orange': %d\n", get(&hashMap, "orange")); return 0; } ``` 这个示例使用了一个简单的哈希函数,将键转换为索引值,并使用该索引将节点存储在哈希表的相应位置。insert函数将给定的键值对插入哈希表,get函数根据键查找对应的值。 请注意,这只是一个简单的示例,实际的哈希表实现可能需要更多的功能和更复杂的哈希函数,以提高性能和处理冲突等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值