Map接口
public interface MyMap
public V put(K k,V v);
public V get(K k);
/**
* 内部类
* @author mama
*
* @param
* @param
*/
public interface Entry
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
private Entry
有一个内部类,定义了Entry
class Entry<K,V>implements MyMap.Entry
K k;
V v;
//指向被this挤压下去的Entry对象
Entry
public Entry(Kk,V v,Entry
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
//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
//将老数组存的内容放到新数组,拿之前散列算法
//3.
againHash(newTable);
}
1.2、把老数组存到新数组的key值进行散列算法,在这之前把value存到list中
private void againHash(MyHashMap
List
//找老数组中的对象
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
//说明遍历的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
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
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;
}