仿写HashMap源码
主要是通过get和put方法来理解Hashmap来理解HashMap如何存键值对,没有涉及太深,红黑树请绕行
先贴出手写的HashMap的demo
一、定义Map接口
public interface Map<K,V> {
/**
* 向Map中插入值
*/
public V put(K k,V v);
/**
* 根据key获取HashMap中的值
*/
public V get(K k);
/**
* 获取集合中,键值对的对象
*/
public int size();
interface Entry<K,V>{
K getKey();
V getValue();
V setValue(V v);
}
}
这个Map接口中只简单的定义了map中的几个主要的函数,用来理解HashMap
二、定义HashMap实现Map接口
public class HashMap<K,V> implements Map<K,V>{
// 集合中的元素个数
private int size;
private static double factory=0.75D;
//扩容,并且重新排列元素
private void resize(){
//翻倍扩容
//1、创建新的array,临时变量
Node<K,V>[] temp=new Node[defaultLength << 1];
//2、重新计算散列值,插入到新的array中去,code=key%defaultLength*2
Node<K,V> node =null;
for (int i=0;i<array.length;i++){
node=array[i];
while(node!=null){
//重新散列
int index=position(node.getKey(),temp.length);
Node<K,V> next=node.next;
node.next=temp[index];
temp[index]=node;
node=next;
}
}
//3、替换老array
array=temp;
defaultLength=temp.length;
temp=null;
}
public void print(){
System.out.println("=====================================");
if(array!=null){
Node<K,V> node=null;
for(int i=0;i<array.length;i++){
node=array[i];
System.out.println("下标["+i+"]");
while(node!=null){
System.out.println("[+"+node.getKey()+":"+node.getValue()+"]");
if(node.next!=null){
node=node.next;
}else{
//尾部元素,跳出循环
node=null;
}
}
System.out.println();
}
}
}
//数据存储的结构
Node<K,V>[] array=null;
//数组/hash桶的长度
private static int defaultLength=16;
@Override
public V put(K k, V v) {
//1、懒加载机制,使用的时候进行分配
if(array==null){
array=new Node[defaultLength];
}
//2、通过hash算法,计算出具体插入的值的位置
int index=position(k,defaultLength);
//3、放入要插入的元素
Node<K,V> node=array[index];
if(node==null){ //如果当前位置没有元素
array[index]=new Node<>(k,v,null);
size++;
}else{
if(k.equals(node.getKey())||k==node.getKey()){
return node.setValue(v);
}else{
array[index]=new Node<>(k,v,node);
size++;
}
}
return null;
}
private int position(K k,int length){
int code =k.hashCode();
//取模算法
return code%(defaultLength-1);
}
@Override
public V get(K k) {
if(array!=null){//先计算出下标,然后通过下标获取值
int index=position(k,defaultLength);
Node<K,V> node=array[index];
//遍历链表
while(node!=null){
//如果key值相同,则返回value
if(node.getKey()==k){
return node.getValue();
}else{
//如果不同,调到下一个元素
node=node.next;
}
}
}
return null;
}
@Override
public int size() {
return this.size;
}
static class Node<K,V> implements Map.Entry<K,V>{
K key;
V value;
Node<K,V> next;
@Override
public K getKey() {
return this.key;
}
@Override
public V getValue() {
return this.value;
}
public Node(K key, V value, Node<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
/**
* 返回原来的旧值
*/
@Override
public V setValue(V v) {
V oldValue=this.value;
this.value=v;
return oldValue;
}
}
}
put方法:
HashMap采用懒加载机制,对HashMap的数组在使用的时候进行分配,map.put("admin","123456"),当向HashMap中put一个键值对时,首先通过这个字符串对象“admin”,调用hashCode方法,计算出它的hashcode值,然后通过hashcode%(数组长度-1)运算,求出你要存放k为admin的下标,然后根据下标进行存,如果该位置没有键值对,直接存下,如果有,那就要考虑你存的值是否重复,有重复则替换,无重复,则把新元素放在该位置,旧值像链表一样连在新元素的后面,最后返回旧值(put方法是有返回值的)
get方法:
同样,首先通过你传入的Key值计算出相应的数组下标值,然后遍历这个数组,查找和你key相等的:key.equals(k),来找出你要get的值
三、Test测试类:
public class Test {
public static void main(String[] args) {
HashMap<String,String> map=new HashMap<>();
map.put("001号","001");
map.put("002号","002");
map.put("003号","003");
map.put("004号","004");
map.put("005号","005");
map.put("006号","006");
map.put("007号","007");
map.put("008号","008");
map.put("009号","009");
map.put("010号","010");
map.put("011号","011");
map.put("012号","012");
map.put("013号","013");
map.put("014号","014");
map.put("015号","015");
map.put("016号","016");
map.put("017号","017");
map.put("018号","018");
map.put("019号","019");
map.put("020号","020");
map.put("021号","021");
map.put("022号","022");
map.put("023号","023");
map.print();
System.out.println(map.get("012"));
System.out.println(map.get("009号"));
}
}