设计一个 HashMap 包括以下几个关键步骤:
- 确定存储结构:HashMap 是键值对的存储结构,因此你需要决定如何表示这两部分数据。常见的方式是使用数组作为存储桶,每个桶中存储一个链表或者红黑树来处理哈希冲突。
- 确定哈希函数:哈希函数的目的是将键映射到数组的索引。一个好的哈希函数应该尽量减少冲突,即不同的键映射到同一个索引的情况。常见的哈希函数包括取余法,乘法哈希,MD5,SHA等。
- 处理哈希冲突:由于哈希函数的有限性,可能会存在不同的键映射到相同的索引,这就是哈希冲突。你可以使用链表、开放寻址法(线性探测、二次探测等)或者更高级的数据结构如红黑树来处理冲突。
- 实现基本操作:
- 插入(Put): 根据哈希函数计算键的索引,然后将键值对插入到相应的桶中。注意处理可能的冲突。
- 查找(Get): 根据哈希函数计算键的索引,然后在相应的桶中查找键对应的值。
- 删除(Remove): 根据哈希函数计算键的索引,然后在相应的桶中删除键值对。
- 确定容量和负载因子:
- 容量(Capacity): 哈希表的大小,一般是桶的数量。可以选择在初始化时指定,或者在达到一定负载因子时进行动态调整。
- 负载因子(Load Factor): 表示哈希表在什么程度上需要进行扩容。负载因子越高,哈希表越满,可能导致性能下降,但空间利用更充分。
- 扩容:当负载因子达到一定阈值时,需要对哈希表进行扩容。扩容通常包括创建一个新的更大的数组,并将所有键值对重新插入到新的数组中。
importjava.util.LinkedList; publicclassMyHashMap<K, V> { privatestaticfinalintDEFAULT_CAPACITY=16; privatestaticfinaldoubleLOAD_FACTOR=0.75; privateLinkedList<Entry<K, V>>[] buckets; privateintsize; publicMyHashMap() { buckets=newLinkedList[DEFAULT_CAPACITY]; } publicvoidput(Kkey, Vvalue) { intindex=getIndex(key); if (buckets[index] ==null) { buckets[index] =newLinkedList<>(); } for (Entry<K, V>entry : buckets[index]) { if (entry.getKey().equals(key)) { entry.setValue(value); return; } } buckets[index].add(newEntry<>(key, value)); size++; if ((double) size/buckets.length>LOAD_FACTOR) { resize(); } } publicVget(Kkey) { intindex=getIndex(key); if (buckets[index] !=null) { for (Entry<K, V>entry : buckets[index]) { if (entry.getKey().equals(key)) { returnentry.getValue(); } } } returnnull; } publicvoidremove(Kkey) { intindex=getIndex(key); if (buckets[index] !=null) { buckets[index].removeIf(entry->entry.getKey().equals(key)); size--; } } privateintgetIndex(Kkey) { returnkey.hashCode() %buckets.length; } privatevoidresize() { LinkedList<Entry<K, V>>[] newBuckets=newLinkedList[buckets.length*2]; for (LinkedList<Entry<K, V>>bucket : buckets) { if (bucket!=null) { for (Entry<K, V>entry : bucket) { intindex=entry.getKey().hashCode() %newBuckets.length; if (newBuckets[index] ==null) { newBuckets[index] =newLinkedList<>(); } newBuckets[index].add(entry); } } } buckets=newBuckets; } privatestaticclassEntry<K, V> { privateKkey; privateVvalue; publicEntry(Kkey, Vvalue) { this.key=key; this.value=value; } publicKgetKey() { returnkey; } publicVgetValue() { returnvalue; } publicvoidsetValue(Vvalue) { this.value=value; } } }
这只是一个简单的示例,实际中还需要考虑并发性能、迭代顺序等方面的问题。Java中已经提供了HashMap类,可以直接使用,上述示例主要是为了说明HashMap的基本原理和实现思路。