手写实现HashMap1.7版本

本文深入探讨了HashMap的工作原理,包括其内部结构、扩容机制、散列算法及存储与查找过程。通过对核心方法put和get的详细解析,帮助读者理解HashMap如何高效地存储和检索数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Map接口

public interface MyMap<K,V> {
public V put(K k,V v);
public V get(K k);

/**
* 内部类
* @author mama
*
* @param <K>
* @param <V>
*/
public interface Entry<K,V>{
public K getKey();
public V getValue();
}
}


HashMap的结构

 

定义的成员变量:

1、定义默认数组的大小  1<<4

private static int defaultLenth=1<<4;

2、定义有限的数组长度空间位置内会形成链表 0.75

private static double defaultAddSizeFactor=0.75;

3、定义数组的位置

private int useSize;

4、定义一个Entry<K,V> table;

private Entry<K,V>[]table=null;

 

有一个内部类,定义了Entry

class Entry<K,V>implements MyMap.Entry<K, V>{

   

K k;

V v;

//指向被this挤压下去的Entry对象

Entry<K,V> next;

public Entry(Kk,V v,Entry<K,V> next) {

this.k=k;

this.v=v;

this.next=next;

}

@Override

public K getKey() {

// TODO Auto-generated method stub

return k;

}

 

@Override

public V getValue() {

// TODO Auto-generated method stub

return v;

}

}

 

定义构造类

1、无参的构造

public MyHashMap() {

this(defaultLenth,defaultAddSizeFactor);

}

2、有参的构造,传入数组的大小和元素占数组总容量形成链表的阈值0.75

public MyHashMap(int length,double defaultAddSizeFactor) {

if(length<0) {

throw new IllegalArgumentException("参数不能为负数"+length);

}

if(defaultAddSizeFactor<=0 || Double.isNaN(defaultAddSizeFactor)) {

throw new IllegalArgumentException("扩容标准必须是大于0的数字"+defaultAddSizeFactor);

}

this.defaultLenth=length;

this.defaultAddSizeFactor=defaultAddSizeFactor;

table=new Entry[defaultLenth];

}

 

 

定义put方法

//1、判断是否需要扩容

if(useSize>defaultLenth*defaultAddSizeFactor) {

//进行两倍的扩容

up2Size();

}

//如果这时扩容了,新数组扩容为2倍,老数组的数据被重新散列到新数组中

int index=getIndex(k,table.length);//得到了下标

Entry<K,V> entry=table[index];

//table[index]的entry为空,说明没有相同的元素,也就形成不了链表

if(entry==null) {

table[index] =new Entry(k,v,null);

useSize++;//使用位置被占用+1

}else if(entry!=null) {//说明说相同的值,挤压下去,形成链表

table[index] =new Entry(k,v,entry);

}

return table[index].getValue();

}

 

1、判断是否需要扩容

if(useSize>defaultLenth*defaultAddSizeFactor) {

//进行两倍的扩容

up2Size();

}

1.1、扩容的方法

private void up2Size() {

Entry<K,V>[] newTable=new Entry[2*defaultLenth];

//将老数组存的内容放到新数组,拿之前散列算法

//3.

againHash(newTable);

}

1.2、把老数组存到新数组的key值进行散列算法,在这之前把value存到list

private void againHash(MyHashMap<K, V>.Entry<K, V>[]newTable) {

List<Entry<K,V>> entryList=new ArrayList<MyHashMap<K,V>.Entry<K, V>>();

//找老数组中的对象

for (int i = 0;i < table.length;i++) {

//老数组没有数据

if(table[i]==null) {

continue;

}

//这个方法存找到不为空的对象存入entryList

foundEntryByNext(table[i],entryList);

}

//进行散列

if(entryList.size()>0) {

useSize=0;

defaultLenth=2*defaultLenth;

table=newTable;

for (Entry<K, V>entry : entryList) {

//说明遍历的EntryList是链表,取消链表结构,把指定的下一个entry。next为null

if(entry.next!=null) {

entry.next=null;//为什么要致为null,因为foundEntryByNext()递归,把数据存到了list中变成了链表,next就不用再指向下一个值了

}

//没有形成链表,调用put

put(entry.getKey(),entry.getValue());

}

}

}

 

1.3、把不为空的对象存到list---使用递归

private void foundEntryByNext(MyHashMap<K, V>.Entry<K, V>entry, List<MyHashMap<K, V>.Entry<K, V>>entryList) {

 if(entry!=null&&entry.next!=null) {

 entryList.add(entry);

 //递归

 foundEntryByNext(entry.next,entryList);

 }else {

 entryList.add(entry);//链表中只有一个

}

}

 

2、通过k和数组的长度范围,获取元素在数组中的位置的方法

private int getIndex(Kk, int length) {

//length 2^n 0000 1111

int m=length-1;

//保证index=【0,length)之间

int index=hash(k.hashCode()&m);//得到数组的位置(进行位运算)

return index;

}

 

2.1、按jdk1.7源码写的hash算法

private int hash(int hashCode) {

hashCode=hashCode^((hashCode>>>20)^(hashCode>>>12));

return hashCode^((hashCode>>>7)^(hashCode>>>4));

}

 

3、得到元素的对象,并且判断为空和不为空

if(entry==null) {

table[index] =new Entry(k,v,null);

useSize++;//使用位置被占用+1

}else if(entry!=null) {//说明说相同的值,挤压下去,形成链表

table[index] =new Entry(k,v,entry);

}

3.1、为空的话,说明没有相同的元素,也就形成不了链表,存入看,k,v和指向链表下一个的值为null,当前位置useSize得自增加一。

3.2、不为空,说明有相同的值,把老值挤压下去,形成链表,存入看,k,v和指向链表下一个

值不为空。

 

 

定义get方法

public V get(Kk) {

int index =getIndex(k,table.length);

if(table[index]==null) {

throw new NullPointerException();//报个空指针异常

}

//处理k存在的情况

return findValueByEqualKey(k,table[index]);

}

 

 

private V findValueByEqualKey(Kk, MyHashMap<K, V>.Entry<K, V> entry) {

if(k==entry.getKey() ||k.equals(entry.getKey())) {

return entry.getValue();

}else if (entry.next!=null) {//key不相等,hash相等

return findValueByEqualKey(k,entry.next);//调用递归找链表的下一个值

}

return null;

}

手写HashMap可以使用C语言进行实现。 一种常见的实现方式是对串行的HashMap加锁,并外层包装一个分段加锁的HashMap实现类似于JDK 1.7之前的ConcurrentHashMap机制的分段式加锁并发哈希表。这样可以提高并发性能,同时保证线程安全。 在实现过程中,可以使用桶排序来提高随机数据下的速度。但是在完全无哈希冲突的数据下,速度可能会稍慢,因为扩容的逻辑比较耗时。可以通过改进扩容逻辑来进一步优化性能。 另外,为了解决链表过长导致的查询效率下降问题,可以使用红黑树来优化。当链表长度超过一定阈值时,将链表转换为红黑树,这样即可以兼顾内存利用率,又可以享受红黑树的高效检索。相比于AVL树,红黑树的插入和删除操作代价更小,因此更适合作为HashMap的底层数据结构。 综上所述,使用C语言可以手写实现一个高效的HashMap,并通过加锁和分段加锁等机制来提高并发性能。在实现过程中,可以使用桶排序和红黑树来优化数据操作效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【手写并发容器】C语言实现 Concurrent HashMap](https://blog.csdn.net/GreyKa/article/details/126326602)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [C语言-手写Map(数组+链表+红黑树)(全功能)](https://blog.csdn.net/weixin_45203607/article/details/126649951)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值