1、HashMap是什么?
HashMap是java常用来存储键值对的数据结构,它是以key/value的形式存储的,它不是线程安全的,Key可以为null值。
2、HashMap的实现原理
HashMap的底层实现主要是基于数组和链表实现的,HashMap中以通过key值的HashCode值来计算Hash值的,通过Hash值来决定存储在数组的位置,也就是说将新元素插入到数组的这个位置来。当插入新元素时,如果新元素的Hash值与数组中这个位置上元素的Hash值相等,就会出现Hash冲突,那么就会将新元素的next指向数组中的这个位置的元素,新元素就插入到这个位置来。
结构图:
3、自己编写HashMap类
Map接口
1 public interface Map{ 2 int size; 3 boolean isEmpty(); 4 Object get(Object key); 5 Object put(Object key,Object value); 6 interface Node{ 7 Object getKey(); 8 Object getValue(); 9 } 10 }
实现HashMap类
public class HashMap implements Map{ publci final int DEFAULT_CPACITY=16; //数组默认容量 private int size=0; Node[] node =new Node[DEFAULT_CPACITY]; public int size(){ return size; } public boolean isEmpty(){ return size==0; } public Object get(Object key){ int keyHashCode=hash(key); int keyIndex=indexOf(keyHashCode,node.length); for(Nodes eNode=node[keyIndex];eNode!=null;eNode=eNode.next){ if(eNode.hashValue==keyHashCode&&eNode.key==key) return eNode.value; } return null; } public Object put(Object key,Object value){ int keyHashCode=hash(key); //获得key的Hash值 int keyIndex=indexOf(keyHashCode,node.length); //通过Hash值获得key在数组中的位置 for(Nodes eNode=node[keyIndex];eNode!=null;eNode=eNode.next){ //遍历key位置下的所有node结点,如果找到了新value替换旧value,并返回旧value if(eNode.hash==keyHashCode&&(eNode.key==key||eNode.key.equals(key))){ Object oldObj=eNode.value; eNode.value=eNode.value; return oldObj; } } addEntry(key,value,keyHashCode,keyIndex); //如果没有找到与key对应的结点,就在链表头新增此结点 return null; } /*在链表头新增结点*/ /*如果超过了原始数组大小,则扩大数组*/ public void addEntry(Object key,Object value,int keyHashCode,int keyIndex){ if(++size==node.length){ resize(2*node.length) } Node nodeNext=node[keyIndex]; node[keyIndex]=new Node(keyHashCode,key,value,nodeNext); } void resize(int newCapacity) { Node[] oldNode = node; int oldCapacity = oldNode.length; Node[] newNode = new Node[newCapacity]; transfer(newNode, initHashSeedAsNeeded(newCapacity)); node= newNode; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } void transfer(Node[] newTable, boolean rehash) { int newCapacity = newTable.length; //for循环中的代码,逐个遍历链表,重新计算索引位置,将老数组数据复制到新数组中去(数组不存储实际数据,所以仅仅是拷贝引用而已) for (Node e : node) { while(null != e) { Node next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); //将当前entry的next链指向新的索引位置,newTable[i]有可能为空,有可能也是个entry链,如果是entry链,直接在链表头部插入。 e.next = newTable[i]; newTable[i] = e; e = next; } } } public int indexOf(int keyHashCode,int length){ return keyHashCode%length; } public int hash(Object key){ return key.hashCode(); } static class Nodes impletents Map.Node{ Object key; Object value; int hash; Nodes next; public Nodes(int hash,Object key,Objectvalue,Nodes next){ this.hash=hash; this.key=key; this.value=value; this.next=next; } public Object getKey(){ return this.key; } public Object getValue(){ return this.value; } }
总结:
实现原理就是数组和链表的结合,数组中存放的是这一组HashCode值最新的一个结点,通过这个结点,以链表的形式存放不同的key值的结点。
在这里简述一下实现步骤:
1:先写好Map接口,表明HashMap中要做的事,声明结点node接口;
2:编写HashMap类实现Map接口;
3:声明结点数组,并实现get和put方法(这里只实现了put和get方法),首先根据Key的值获得key对应的HashCode值,再根据HashCode值找到在数组中的下标,这个位置就是保存所有HashCode值为这个值的首结点,也就是链表头。
4:实现get方法,通过链表头一个个遍历打到对应的key,并返回对应value,如果没有对应key,则返回空。
5:实现put方法,也是先遍历链表,如果存在对应的key,则新值替换旧值并返回旧值。如果不存在,则添加新值到链表头(单向链表添加节点最快的方式),在添加节点之前如果size达到了临界值,就需要对数组进行扩容(新建一个数组,并将所有数据拷贝到新数组,如果数组进行扩容,数组长度发生变化,而存储位置 index = hashCode%node.length,index也可能会发生变化,需要重新计算index)。
其它属性和方法也都是通过这种获得链表头,遍历链表的形式进行的。
如果想要看更加详细的HashMap的原理,可参照博客:http://www.cnblogs.com/chengxiao/p/6059914.html#t3