手写hash表

简介: 以前一直用hash表,但是并不清楚它的原理。所以,我决定手写一个hash表,彻底搞清它的工作方式。这里有一个误区是:通常我们会认为对象数组是存储对象本身,也就是对象数组是开辟一个空间,然后将对象放进去。

以前一直用hash表,但是并不清楚它的原理。所以,我决定手写一个hash表,彻底搞清它的工作方式。这里有一个误区是:通常我们会认为对象数组是存储对象本身,也就是对象数组是开辟一个空间,然后将对象放进去。这不仅从jvm的角度来解释(java对象创建在堆上),而且自己认真想想就不是这样。对象数组其实保存的是对象的引用而已。为什么要说这么简单的事,因为当初确实理解这个hashmap的时候搞糊涂了。有一段代码可以证明保存的是引用:

/**
 *最后输出的是“改变”,说明存储的是内存地址(不然没有修改数组中的对象数组中对象为什么会发生改变)
 */
public class TestArray {
    public static void main(String[] args) {
        Item[] items = new Item[3];
        Item item = new Item();
        item.setName("123");
        items[0] = item;
        item.setName("改变");
        System.out.println(items[0].getName());
    }
}

class Item {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Item{" +
                "name='" + name + '\'' +
                '}';
    }
}

说到hash表,其实他是通过一个函数输入key算value的地址在哪。业界采用的比较多的方式是链地址法。

img_d93f52e0285132c487d5bfd0481427d4.png
链地址法.png

这里只是说了个大概的结构,如果想要看详细的过程,可以看 这里
这个方法的好处是,结合了数组和链表的优势,抵消hash冲突带来的劣势。每个链表节点都有key,value,next节点。工作流程:先通过key算地址得到f(key),找到链表的头,然后将key的值与每个节点key的值比较,如果key相同,将value值返回。
代码如下:

class entry<k, v>{
    int capacity;
    node[] no;

    public entry(int n){
        capacity = n;
        no = new node[n];
    }

    //链表类
    class node<k, v>{
        k key;
        v value;
        node<k, v> next;

        public node(){}

        node(k key, v value, node<k, v> next){
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    public int hashcode(k key){
        return (key.hashCode() & 0x7ffffff) % capacity;
    }

    public void put(k key, v value){
        int h = hashcode(key);
        for(node<k, v> n = no[h]; n != null; n = n.next){
            if(key.equals(n.key)){
                n.value = value;
                return;
            }
        }

        node<k, v> old = no[h];//插入的时候应该是这么插,将新节点的地址放到数组中,然后让新节点指向原来第一个节点
        no[h] = new node(key, value, old);
    }

    public v get(k key){
        int h = hashcode(key);
        if(no[h] == null){
            return null;
        }

        for(node<k, v> n = no[h]; n != null; n = n.next){
            if(key.equals(n.key))
                return n.value;
        }
        return null;
    }

    public static void main(String[] args) {
        entry<Integer, Integer> map = new entry<Integer, Integer>(100);

        map.put(1, 90);
        map.put(2, 95);

        System.out.println(map.get(1));
        System.out.println(map.get(2));
    }

}
目录
相关文章
|
5天前
|
存储 算法 数据安全/隐私保护
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
7 0
|
5天前
|
存储 缓存 算法
【数据结构查找算法篇】----散列查找【实战项目】
【数据结构查找算法篇】----散列查找【实战项目】
46 10
|
5天前
|
存储 算法 Java
数据结构与算法面试题:实现一个哈希表,并考虑哈希冲突的解决方案。
数据结构与算法面试题:实现一个哈希表,并考虑哈希冲突的解决方案。
23 0
|
5天前
|
算法
【数据结构】盘点那些经典的 [哈希面试题]【哈希切割】【位图应用】【布隆过滤器】(10)
【数据结构】盘点那些经典的 [哈希面试题]【哈希切割】【位图应用】【布隆过滤器】(10)
|
5天前
|
存储 JavaScript 前端开发
一文带你手写数据结构 链表篇
一文带你手写数据结构 链表篇
42 0
|
5天前
手写数据结构 栈篇
手写数据结构 栈篇
20 0
|
5天前
|
存储 前端开发
手写数据结构 队列篇
手写数据结构 队列篇
35 0
|
7月前
|
存储 缓存 安全
【JavaSE专栏53】Java集合类HashMap解析,基于哈希表的键值对存储结构
【JavaSE专栏53】Java集合类HashMap解析,基于哈希表的键值对存储结构
|
10月前
|
存储 算法
|
11月前
|
网络协议 Java 数据库连接
HashMap源码手写简易篇(数组+链表)
HashMap源码手写简易篇(数组+链表)