手写HashMap

相关概念:
什么叫hash:就是把一个不固定的长度的二进制值映射成固定长度的二进制值
hash算法:就是对应这个映射规则
hash值:固定长度的二进制值


什么叫hash表:就是一个数组表(talbe),数组里面存储是的是(entry对象,每个entry对象里面的内容是key vlaue next指针):key value next指针 | key value next指针 | ...
什么是hash算法:
1.除留余数法 key % table.length  = index (对应的hashtable的索引)

2.冲突解决:链表解决,后面进来的元素占用原来元素的位置,并且第二个的指针指向第一个.
table.length =10
对应的hash值key.hashCode()= 2
2%10 = 2

另一个key对应的hash值key.hashCode() = 12

12%10 = 2 也等于2


代码部分:
package com.yz.hashmap;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义HashMap
 * Created by yz on 2018/2/24.
 */
public class YZHashMap<K,V> implements YZMap<K,V>{
    /**
     * hash表的默认长度
     */
    private static int defaultLength = 16;

    /**
     * 负载因子 就是一个阈值  扩容 如果表的长度超过了0.75,既16*0.75=12 就要扩容了
     */
    private static double defalutLoader = 0.75;

    //存储数据的地方
    private Entry<K,V>[] table = null;

    //表里面元素个数 表存储几个元素了
    private int size = 0;

    /**
     * 有参构造方法 初始化hash表的默认长度 ,负载因子,table长度
     * @param length
     * @param loader
     */
    YZHashMap(int length ,double loader){
        this.defaultLength = length;
        this.defalutLoader =loader;
        table = new Entry[defaultLength];
    }

    /**
     * 无参构造函数 调用有参构造
     */
    YZHashMap(){
        this(defaultLength,defalutLoader);
    }


    /**
     * 需要把k v 数据存储到hash表里面
     * @param k
     * @param v
     * @return
     */
    public V put(K k, V v) {

        //判断表的长度是否需要扩容
        if(size >= defaultLength * defalutLoader){
            up2size();
        }

        int index = getIndex(k);
        //根据表中位置取对象
        Entry<K, V> entry = table[index];
        if(entry == null){
            //第一次存储
            table[index] = newEntry(k,v,null);
            size++;
        }else{
            //如果该位置已经存在数据了,占用原数据的位置,并将指针指向原数据(entry)  这个两个entry的index的位置一样,但是指针不一样了
            table[index] = newEntry(k,v,entry);
        }
        return table[index].getValue();
    }


    /**
     * 扩容,标准扩一倍 长度16扩32
     */
    private void up2size(){
        //扩容以后,老表原来的数据的位置要重新计算,因为index是根据key的hashCode % 表(不管是新表还是老表)的长度获取的值 int index = k.hashCode() % m;  再散列,从新分配index位置
        Entry<K,V>[] newTable = new Entry[2 * defaultLength];
        againHash(newTable);
    }

    /**
     * 扩容后再散列,从新分配老表位置
     * @param newTable
     */
    private void againHash(Entry<K,V>[] newTable){
        //1.拿到前面老表的数据
        ListK,V>> list = new ArrayListK, V>>();
        for(int i=0;i<table.length;i++){
            if(table[i] ==  null){
                continue;
            }
            findEntryByNext(table[i],list);
        }
        //获取到数据
        if(list.size() > 0){
            //初始化
            size = 0 ;
            defaultLength = defaultLength * 2;
            table = newTable;

            for(Entry<K,V> entry : list){
                if(entry.next != null){
                    entry.next = null;
                }
                //将原数据全部存储到扩容的数组中
                put(entry.getKey(),entry.getValue());
            }
        }
    }

    /**
     * 获取老表的全部元素,含链表元素,因为老表可能存在链表结构,递归免不了的
     * next指向的就是entry对象
     * @param entry
     * @param list
     */
    private void findEntryByNext(Entry<K,V> entry,ListK,V>> list){
        if(entry != null && entry.next != null){
            list.add(entry);
            //如果链表下面还有元素,使用递归
            findEntryByNext(entry.next,list);
        }else{
            list.add(entry);
        }
    }


    /**
     * 实例化Entry对象
     * @param k
     * @param v
     * @param next
     * @return
     */
    private Entry<K,V> newEntry(K k,V v,Entry<K,V> next){
        return new Entry(k,v,next);
    }

    /**
     * hash算法 获取key对应的索引,根据key的hash值对表的长度取模,得到的index就是在表中的存储位置
     * @param k
     * @return
     */
    private int getIndex(K k){
        int m = defaultLength;
        int index = k.hashCode() % m;
        return index >=0 ? index : -index;
    }

    /**
     * 根据key递归去找对应的value值
     * @param k
     * @return
     */
    public V get(K k) {

        int index = getIndex(k);

        if(table[index] == null){
            return null;
        }
        return findValueByEqualKey(k,table[index]);
    }

    /**
     * 链表中index一样情况下,entry的key不一样
     * 根据key拿到对应的数据
     * @param k
     * @param entry
     * @return
     */
    private V findValueByEqualKey(K k , Entry<K,V> entry){
        if(k == entry.getKey() || k.equals(entry.getKey())){
            return entry.getValue();
        }else{
            //如果不相等,继续找,使用递归
            if(entry.next != null){
                return findValueByEqualKey(k,entry.next);
            }
        }
        return null;
    }

    public int size() {
        return 0;
    }

    /**
     * 实现YZMap接口内部类
     */
    class Entry<K,V> implements YZMap.Entry<K,V>{

        K k;
        V v;
        //next 指针指向另一个对象
        Entry<K,V> next;

        //构造方法,初始化三个值
        Entry(K k,V v,Entry<K,V> next){
            this.k = k;
            this.v = v;
            this.next = next;
        }

        public K getKey() {
            return k;
        }

        public V getValue() {
            return v;
        }
    }

}

package com.yz.hashmap;

/**
 * 自定义map接口
 * Created by yz on 2018/2/24.
 */
public interface YZMap<K,V> {
    //把key value属性包装成对象存储到hash表中
    V put(K k,V v);

    //根据key获取value
    V get(K k);

    //获取表的长度
    int size();

    /**
     * 内部接口,entry对象实际上就是存储到hash表里面的数据对象
     * key vlaue next
     * @param 
     * @param 
     */
    interface Entry<K,V>{
        //获取key
        K getKey();

        //获取value
        V getValue();
    }
}
package com.yz.hashmap;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by yz on 2018/2/24.
 */
public class MyTest {
    public static void main(String[] args) {
        Map jdkmap = new HashMap();
        Long t1 = System.currentTimeMillis();
        for(int i=0;i<1000;i++){
            jdkmap.put("yz"+i,"test"+i);
        }
        for(int i=0;i<1000;i++){
            System.out.println(jdkmap.get("yz"+i));
        }
        Long t2 = System.currentTimeMillis();
        System.out.println("jdk time: "+(t2-t1));
        System.out.println("------------------------");

        YZMap yzmap = new YZHashMap();
        Long t3 = System.currentTimeMillis();
        for(int i=0;i<1000;i++){
            yzmap.put("yz"+i,"test"+i);
        }
        for(int i=0;i<1000;i++){
            System.out.println(yzmap.get("yz"+i));
        }
        Long t4 = System.currentTimeMillis();
        System.out.println("yz time: "+(t4-t3));
    }
}
 
  


你可能感兴趣的:(手写HashMap)