【基础决定高度】【数据结构】从零开始手写HashMap简易版(70行代码实现存储取值功能)

写在前面:
今天电话面试阿里,本来就 不抱希望。所以面试的时候问,我要怎么样发展,才可以复合阿里高级JAVA的要求呢?
面试官说,对于高级JAVA,要求基础好。
啥是基础,按我的理解,就是
1.数据结构
2.算法
3.JAVA基础
4.Spring源码

在十几年的学生生涯里,我深刻地明白:慢就是快——只有开始慢下来,后面才能快起来;少就是多——贪多永远就是泛泛而谈;专就是博——只有有一个“专”,才有立身之基,可以辐射到四周。
总而言之,为了更高的高度,必须夯实基础。
立个Flag,每周四篇博客,踏踏实实,每天进步一点!

正文:
啥是HashMap?
HashMap用的很多,用起来也很方便。它是怎么做到的呢?
我们首先看思路:
https://www.cnblogs.com/yuanblog/p/4441017.html
这篇文章写得很好,里面有句话说明白了HashMap的本质:
一个元素是链表的数组

// 这是HashMap中的部分源码,可以看到这个Node节点有我们熟悉的<K,V>键值对,并且还指向下一个节点
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

我们如果想写一个简单的HashMap,只需要完成它的数据结构就可以了,
也就是说
1.建立一个 链表数组
2.通过hash分配节点在哪个链表中
3.判断是更新key值还是在链表中插入

。。。

开始造轮子!

一、写一个K,V节点,这里为了体现山寨性,泛型用A,B

public class MyEntry<A,B> {
    A a;
    B b;
    int hash;
    MyEntry<A,B> next;

    public MyEntry(){

    }
    public MyEntry(A a,B b){
        this.a = a;
        this.b = b;
    }
}

二、MyHashMap的一些属性和方法

所谓方法未定,接口先行。因为是一个简易山寨版,所以我们目前只支持存和取两个方法。

public class MyHashMap<A,B> {
    static final int MY_DEFAULT_INITIAL_CAPACITY = 8;
    MyEntry<A,B>[] myTable;
    int myCapacity;
	// 默认容量
    public MyHashMap(){
        this.myCapacity = MY_DEFAULT_INITIAL_CAPACITY;;
    }
	// 指定容量
    public MyHashMap(int capacity) {
        this.myCapacity = capacity;
    }
    
	// hash 的最简单实现,直接模(容量-1) 得到数组下标
    private int myHash(A a) {
        return a.hashCode() % (myCapacity - 1);
    }
    
    public B put(A a, B b) {
        return b;
    }
    
    public B get(A a) {
        return null;
    }
}

三、方法的具体实现

put方法完全体

 public B put(A a, B b) {
        int hash = myHash(a); // 计算hash
        if (null == myTable) {
        // myTable为空则初始化
            myTable = new MyEntry[myCapacity];
        }
        // 如果这个地方就是空的话,直接放进去就可以了
        if (null == myTable[hash]) {
            myTable[hash] = new MyEntry<>(hash,a,b);
        } else {
            // 不是空,需要考虑key是否已经存在
            // 存在则在原有key上更新value
            // 不存在,则采取尾插
            reSetValueOrInsert(a,b,myTable[hash]);
        }
        return b;
    }
    
// 如果含有这个key则重置值
private void reSetValueOrInsert(A a,B b, MyEntry myEntry) {
        // 如果相等,则更新value并返回true
        if (myEntry.a.equals(a)) {
            myEntry.b = b;
            return;
        }
         // 不相等且已经是最后一个节点了,
         // 把新的键值对插在最后
        if (myEntry.next == null) {
        	myEntry.next = new MyEntry(myHash(a),a,b)
            return;
        }
        // 都不满足则走到下个节点
        reSetValueOrInsert(a, b, myEntry.next);
    }

get方法完全体

	public B get(A a) {
	// 如果myTable 为空直接返回null
        return myTable == null ? null : getValue(a);
    }
	// 否则根据a去链表中匹配
    private B getValue(A a) {
        int hash = myHash(a);
        MyEntry it = myTable[hash];
        while (null!=it && it.a.equals(a)) {
            return (B)it.b;
        }
        return null;
    }

四、完整的MyHashMap(存储取值实现)类

package com.ale.Try4st.myCollections;

public class MyHashMap<A,B> {
    static final int MY_DEFAULT_INITIAL_CAPACITY = 8;
    MyEntry<A,B>[] myTable;
    int myCapacity;

    public MyHashMap(){
        this.myCapacity = MY_DEFAULT_INITIAL_CAPACITY;;
    }

    public MyHashMap(int capacity) {
        this.myCapacity = capacity;
    }


    private int myHash(A a) {
        return a.hashCode() % (myCapacity - 1);
    }

    public B put(A a, B b) {
        int hash = myHash(a);
        if (null == myTable) {
            myTable = new MyEntry[myCapacity];
        }
        // 如果这个地方就是空的话,直接放进去就可以了
        if (null == myTable[hash]) {
            myTable[hash] = new MyEntry<>(hash,a,b);
        } else {
            // 不是空,需要考虑key是否已经存在
            // 存在则在原有key上更新value
            // 不存在,则尾插
            reSetValueOrInsert(a,b,myTable[hash]);
        }
        return b;
    }


    private void reSetValueOrInsert(A a,B b, MyEntry myEntry) {
        // 如果相等,则更新value并返回true
        if (myEntry.a.equals(a)) {
            myEntry.b = b;
            return;
        }
        // 不相等且已经是最后一个节点了,
        // 把新的键值对插在最后
        if (myEntry.next == null) {
            myEntry.next = new MyEntry(myHash(a),a,b);
            return;
        }
        // 都不满足则走到下个节点
        reSetValueOrInsert(a, b, myEntry.next);
    }

    public B get(A a) {
        return myTable == null ? null : getValue(a);
    }

    private B getValue(A a) {
        int hash = myHash(a);
        MyEntry it = myTable[hash];
        while (null!=it && it.a.equals(a)) {
            return (B)it.b;
        }
        return null;
    }
}

基本功能测试

public class MyHashMapTest {
    public static void main(String[] args) {
        MyHashMap<String,String> myHashMap = new MyHashMap<>();
        System.out.println(myHashMap.put("阿乐","还行"));
        System.out.println(myHashMap.put("阿乐","不错"));
        System.out.println(myHashMap.put("阿乐","很强"));
        System.out.println(myHashMap.put("小胖","小笨蛋"));
        System.out.println(myHashMap.get("阿乐"));
        System.out.println(myHashMap.get("小胖"));
        System.out.println(myHashMap.get("xiaopang"));
    }
}

输出结果
还行
不错
很强
小笨蛋
很强
小笨蛋
null

总结:

只需要70行代码就可以写一个HashMap的简化版出来,当然,这个简化版有很多阉割的地方。
后面有时间会写一个进阶版出来。

------------------------我是阿乐,CV熟练工,会一点JAVA------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值