HashMap源码解析(前奏)

本文深入解析HashMap的原理,探讨其数组+链表的数据结构和散列算法。通过手写一个简易版的HashMap,展示了如何实现高效的增删改查。分析过程中,强调了HashMap如何结合数组和链表的优点,以及如何处理哈希碰撞。最后鼓励读者对比自定义HashMap与系统源码,进一步加深理解。
摘要由CSDN通过智能技术生成

hashmap源码解析可以说是面试中被问及频率比较高的了,主要还是因为hashmap的设计思路比较好,效率高。我今天不分析hashmap的扩容等内容,只分析hashmap的原理内容,以及手写一个hashmap。

hashmap采用的数据结构是数组+链表,数组特点是增删效率低,但是查找(根据下角标查找)效率高,当然如果数有序数组则根据二分查找等算法效率也很高,而链表的特点是,增删效率高,但是查找需要遍历效率低(改为树结构后查找效率也很高了)。所以,我们能不能把二者的各自有点结合一下呢?既有数组的高效查找又有链表的高效增删,答案是有的,那就是hashmap。先说hash,hash是散列算法,指的是任意长度的内容经过计算后生成一个固定长度的内容,这个过程就是hash,比如MD5算法。如果两个内容的hash结果一样,我们成为hash碰撞,根据这个算法定义我们可以知道hash碰撞是不可避免的,所以我们能做的就是如何降低hash碰撞。好了,说完了hash,我们再回头看看hashmap,数组+链表,如下图:

 看到这个图,再回头想想散列函数的理解,什么是散列?就是将数据分散开来,hashmap中hash就是将数据分到不同的数据中,然后每个数组中存放的是什么?链表。所以再把数据放到链表中保存起来。那么如何分组?就是hash算法,最简单的方法也是hashmap中用的方法,就是将数据的某个唯一的值取模数组的长度,得到的结果就是数组的下角标,这样就把数据初步确定了要放到哪个链表中了,是不是很简单,也很精妙!那这样比我们直接放到链表中保存数据,查找速度快多少呢?数组的长度倍!

        好了,那么有了上面的分析,我们先手写一个简易版的hashmap!

        1.定义节点,我使用的是双向链表,目的是为了添加效率更高

class HashData {
    private int id;
    private String name;
    public HashData next;
    public HashData pre;

    public HashData(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

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

          2.定义双向链表,以及链表的增删改查

class CusLinkData {
    //头结点,默认为空
    private HashData mHead;
    private HashData mLast;

    /**
     * 添加数据
     *
     * @param item
     */
    public void add(HashData item) {
        if (mHead == null) {
            mHead = item;
            mLast = mHead;
            return;
        }
        HashData search = search(item.getId());
        if (null == search) {
            mLast.next = item;
            item.pre = mLast;
            mLast = item;
        } else {
            updata(search);
        }
    }

    /**
     * 删除节点
     *
     * @param item
     */
    public void remove(HashData item) {
        if (null == mHead) {
            return;
        }
        HashData temp = mHead;
        while (true) {
            if (temp.getId() == item.getId()) {//找到了
                break;
            }
            if (temp.next == null) {//没找到
                return;
            }
            temp = temp.next;
        }
        if (mHead == temp) {//头节点
            HashData headNext = mHead.next;
            mHead.next = null;
            if (null != headNext) {
                headNext.pre = null;
            }
            mHead = headNext;
        } else { //A   B   C
            HashData t = temp.pre;
            temp.pre.next = temp.next;
            if (null != temp.next)
                temp.next = t;
            temp.pre = null;
            temp.next = null;
        }
    }

    /**
     * 查找节点
     *
     * @param id
     * @return
     */
    public HashData search(int id) {
        if (null == mHead) {
            return null;
        }
        HashData temp = mHead;
        while (true) {
            if (temp.getId() == id)
                return temp;
            if (null == temp.next) {
                return null;
            }
            temp = temp.next;
        }
    }

    /**
     * 更新节点
     *
     * @param item
     */
    public void updata(HashData item) {
        HashData hashData = search(item.getId());
        if (null == hashData) {
            return;
        }
        hashData.setName(item.getName());
    }

    /**
     * 打印链表所有数据
     */
    public void printAllData() {
        if (mHead == null) {
            return;
        }
        HashData temp = mHead;
        while (true) {
            System.out.print(temp.toString() + "    ");
            if (null == temp.next) {
                return;
            }
            temp = temp.next;
        }
    }


}

        3.自定义hashmap登场了,hashmap中定义链表数组和hash算法,然后也有增删改查方法就可以了。

class CusHashMap {
    private int mArrSize = 16;
    private CusLinkData[] mDataArr;

    public CusHashMap() {
        mDataArr = new CusLinkData[mArrSize];
        initData();
    }

    public CusHashMap(int size) {
        this.mArrSize = size;
        mDataArr = new CusLinkData[mArrSize];
        initData();
    }

    private void initData() {
        for (int i = 0; i < mArrSize; i++) {
            mDataArr[i] = new CusLinkData();
        }
    }

    public void add(HashData item) {
        int index = hash(item);
        mDataArr[index].add(item);
    }

    public void remove(HashData item) {
        int index = hash(item);
        mDataArr[index].remove(item);
    }

    public HashData search(HashData item) {
        int index = hash(item);
        return mDataArr[index].search(item.getId());
    }

    public void updata(HashData item) {
        int index = hash(item);
        mDataArr[index].updata(item);
    }


    /**
     * 散列函数计算方式,确定在那个分组
     *
     * @param item
     * @return
     */
    private int hash(HashData item) {
        return item.getId() % mArrSize;
    }

    public void print() {
        for (int i = 0; i < mArrSize; i++) {
            System.out.print("第" + (i + 1) + "个链表的数据为 ");
            mDataArr[i].printAllData();
            System.out.println();
        }
    }
}

      我们可以看到,其实自定义hashmap中的增删改查,就做了两件事,第一找到节点所在分组,第二操作所在链表对应的增删改查。好了,简单不!!!

      好了,上面我们自定义的hashmap分析完了,然后再对照系统hashmap源码,分析源码去吧。一一对照,还是很简单的。

     

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值