手动实现HashMap

HashMap是由数组和链表来实现对数据的存储,它们各有特点:

  • 数组:占用空间连续,寻址容易,查询速度快。但是,增加和删除效率 非常低。
  • 链表:占用空间不连续,寻址困难,查询速度慢。但是,增加和删除效率非常高。

HashMap通过使用”哈希表“结合了数组和链表的优点,哈希表的本质就是“数组+链表”。

手动实现HashMap

1.创建结点类

package cn.GTMStudio.MyConnection;

/**
 * 用于GTMHashMap中
 */
public class Node2 {
    int hash;
    Object key;
    Object value;
    //下一个结点
    Node2 next;

}

2. 实现了put方法增加键值对,并解决了键重复时覆盖相应的结点

package cn.GTMStudio.MyConnection;

/**
 * 自定义一个HashMap
 * 实现了put方法增加键值对,并解决了键重复时覆盖相应的结点
 *
 * @author henrly
 */
@SuppressWarnings("all")
public class GTMHashMap01 {
    Node2[] table;//位桶数组
    int size;//存放的键值对的个数

    public GTMHashMap01() {
        this.table = new Node2[16];//长度一般定义为2的整数幂
    }

    public void put(Object key, Object value) {
        //定义新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

        Node2 temp = table[newNode.hash];
        boolean keyRepeat = false;
        Node2 iterLast = null;//正在遍历的最后一个元素
        if (temp == null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
        } else {
            //此处数组元素不为空,则遍历对应链表
            //位桶数组的作用体现
            while (temp != null) {
                //判断key,如果重复,则覆盖
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value;//只是覆盖value即可,其它的值(hash,key,next)保持不变
                    break;//结束循环
                } else {
                    //key不重复
                    iterLast = temp;//保存最后一个元素
                    temp = temp.next;
                }
            }
            if (!keyRepeat) {
                //若没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;//插入链表
            }


        }
    }

    /**
     * 计算hash值
     *
     * @param v
     * @param length
     * @return
     */

    public static int myHash(int v, int length) {
        //直接位运算,效率高
        System.out.println("hash in myHash: " + (v & (length - 1)));
        return v & (length - 1);
    }

    public static void main(String[] args) {
        GTMHashMap01 m = new GTMHashMap01();
        m.put(10, "aa");
        m.put(20, "bb");
        m.put(30, "cc");
        m.put(20, "sssss");
        m.put(53, "533");
        m.put(69, "69999");
        m.put(85, "8555");

        System.out.println(m);


    }
}

3. 实现toString方法,方便查看Map中的键值对信息

package cn.GTMStudio.MyConnection;

/**
 * 自定义一个HashMap
 * 实现toString方法,方便查看Map中的键值对信息
 *
 * @author henrly
 */
@SuppressWarnings("all")
public class GTMHashMap02 {
    Node2[] table;//位桶数组
    int size;//存放的键值对的个数

    public GTMHashMap02() {
        this.table = new Node2[16];//长度一般定义为2的整数幂
    }

    public void put(Object key, Object value) {
        //定义新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

        Node2 temp = table[newNode.hash];
        boolean keyRepeat = false;
        Node2 iterLast = null;//正在遍历的最后一个元素
        if (temp == null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
            size++;
        } else {
            //此处数组元素不为空,则遍历对应链表
            //位桶数组的作用体现
            while (temp != null) {
                //判断key,如果重复,则覆盖
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value;//只是覆盖value即可,其它的值(hash,key,next)保持不变
                    break;//结束循环
                } else {
                    //key不重复
                    iterLast = temp;//保存最后一个元素
                    temp = temp.next;
                }
            }
            if (!keyRepeat) {
                //若没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;//插入链表
                size++;
            }


        }
    }

    @Override
    public String toString() {
        //{10:aa,20:bb}
        StringBuilder sb = new StringBuilder("{");
        //遍历位桶数组
        for (int i = 0; i < table.length; i++) {
            Node2 temp = table[i];
            //遍历链表
            while (temp != null) {
                sb.append(temp.key + ":" + temp.value + ",");
                temp = temp.next;
            }
        }
        sb.setCharAt(sb.length() - 1, '}');
        return sb.toString();
    }

    /**
     * 计算hash值
     *
     * @param v
     * @param length
     * @return
     */

    public static int myHash(int v, int length) {
        //直接位运算,效率高
        System.out.println("hash in myHash: " + (v & (length - 1)));
        return v & (length - 1);
    }

    public static void main(String[] args) {
        GTMHashMap02 m = new GTMHashMap02();
        m.put(10, "aa");
        m.put(20, "bb");
        m.put(30, "cc");
        m.put(20, "sssss");
        m.put(53, "533");
        m.put(69, "69999");
        m.put(85, "8555");

        System.out.println(m);
    }
}

4. 实现get方法,根据键对象获取对应的值对象

package cn.GTMStudio.MyConnection;

/**
 * 自定义一个HashMap
 * 实现get方法,根据键对象获取对应的值对象
 *
 * @author henrly
 */
@SuppressWarnings("all")
public class GTMHashMap03 {
    Node2[] table;//位桶数组
    int size;//存放的键值对的个数

    public GTMHashMap03() {
        this.table = new Node2[16];//长度一般定义为2的整数幂
    }

    public Object get(Object key) {
        //获取hash值
        int hash = myHash(key.hashCode(), table.length);
        Node2 temp = table[hash];
        Object value = null;
        //遍历链表
        while (temp != null) {
            if (temp.key.equals(key)) {
                value = temp.value;
                break;
            } else {
                temp = temp.next;
            }
        }
        return value;
    }

    public void put(Object key, Object value) {
        //如果要完善,还需要考虑数组扩容的问题!!!

        //定义新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

        Node2 temp = table[newNode.hash];
        boolean keyRepeat = false;
        Node2 iterLast = null;//正在遍历的最后一个元素
        if (temp == null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
            size++;
        } else {
            //此处数组元素不为空,则遍历对应链表
            //位桶数组的作用体现
            while (temp != null) {
                //判断key,如果重复,则覆盖
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value;//只是覆盖value即可,其它的值(hash,key,next)保持不变
                    break;//结束循环
                } else {
                    //key不重复
                    iterLast = temp;//保存最后一个元素
                    temp = temp.next;
                }
            }
            if (!keyRepeat) {
                //若没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;//插入链表
                size++;
            }


        }
    }

    @Override
    public String toString() {
        //{10:aa,20:bb}
        StringBuilder sb = new StringBuilder("{");
        //遍历位桶数组
        for (int i = 0; i < table.length; i++) {
            Node2 temp = table[i];
            //遍历链表
            while (temp != null) {
                sb.append(temp.key + ":" + temp.value + ",");
                temp = temp.next;
            }
        }
        sb.setCharAt(sb.length() - 1, '}');
        return sb.toString();
    }

    /**
     * 计算hash值
     *
     * @param v
     * @param length
     * @return
     */

    public static int myHash(int v, int length) {
        //直接位运算,效率高
        return v & (length - 1);
    }

    public static void main(String[] args) {
        GTMHashMap03 m = new GTMHashMap03();
        m.put(10, "aa");
        m.put(20, "bb");
        m.put(30, "cc");
        m.put(20, "sssss");
        m.put(53, "533");
        m.put(69, "69999");
        m.put(85, "8555");

        System.out.println(m);
        System.out.println(m.get(53));

    }
}

5.完善封装,增加泛型

package cn.GTMStudio.MyConnection;

import java.util.Map;

/**
 * 自定义一个HashMap
 * 完善封装,增加泛型
 *
 * @author henrly
 */
@SuppressWarnings("all")
public class GTMHashMap04<K,V> {
    Node3[] table;//位桶数组
    int size;//存放的键值对的个数

    public GTMHashMap04() {
        this.table = new Node3[16];//长度一般定义为2的整数幂
    }

    public V get(K key) {
        //获取hash值
        int hash = myHash(key.hashCode(), table.length);
        Node3 temp = table[hash];
        V value = null;
        //遍历链表
        while (temp != null) {
            if (temp.key.equals(key)) {
                value = (V)temp.value;
                break;
            } else {
                temp = temp.next;
            }
        }
        return value;
    }

    public void put(K key, V value) {
        //如果要完善,还需要考虑数组扩容的问题!!!

        //定义新的节点对象
        Node3 newNode = new Node3();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;

        Node3 temp = table[newNode.hash];
        boolean keyRepeat = false;
        Node3 iterLast = null;//正在遍历的最后一个元素
        if (temp == null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
            size++;
        } else {
            //此处数组元素不为空,则遍历对应链表
            //位桶数组的作用体现
            while (temp != null) {
                //判断key,如果重复,则覆盖
                if (temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value;//只是覆盖value即可,其它的值(hash,key,next)保持不变
                    break;//结束循环
                } else {
                    //key不重复
                    iterLast = temp;//保存最后一个元素
                    temp = temp.next;
                }
            }
            if (!keyRepeat) {
                //若没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;//插入链表
                size++;
            }


        }
    }

    @Override
    public String toString() {
        //{10:aa,20:bb}
        StringBuilder sb = new StringBuilder("{");
        //遍历位桶数组
        for (int i = 0; i < table.length; i++) {
            Node3 temp = table[i];
            //遍历链表
            while (temp != null) {
                sb.append(temp.key + ":" + temp.value + ",");
                temp = temp.next;
            }
        }
        sb.setCharAt(sb.length() - 1, '}');
        return sb.toString();
    }

    /**
     * 计算hash值
     *
     * @param v
     * @param length
     * @return
     */

    public static int myHash(int v, int length) {
        //直接位运算,效率高
        return v & (length - 1);
    }

    public static void main(String[] args) {
        GTMHashMap04<Integer,String> m = new GTMHashMap04();
        m.put(10, "aa");
        m.put(20, "bb");
        m.put(30, "cc");
        m.put(20, "sssss");
        m.put(53, "533");
        m.put(69, "69999");
        m.put(85, "8555");
        System.out.println(m);
        System.out.println(m.get(53));

    }
}

总结

HashMap底层是由数组和链表实现的,对HashMap的操作实际上就是先遍历数组再遍历链表,掌握这一点就比较容易掌握HashMap。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值