哈希表(Java)

哈希表结合了顺序表和链表两者的优势,顺序表随机访问快,链表插入删除元素快。那么怎么将两者结合呢?
首先想要随机访问速度快,必须用顺序表,试想一下一个场景:
1.场景一
A往数组里填充了几个元素:4,2,66,89,1
将这个数组交给了B
B拿到数组后,想要获取元素:1,但是B不知道元素1具体在哪个索引下,所以B只能遍历数组,从而获取到了元素:1
时间复杂度 O(n) n为数组大小

2.场景二
B显然不愿意这样干,和A商量出了另一种方式:B先告诉A一个数字范围,他需要知道0-100之间的数字是否存在数组里
A初始化100长度的数组,然后将4,2,66,89,1索引下的值改为1,并将数组交给B
B获取到数组后,想要知道数组里是否有66,只需要判断下数组66索引下的值是否为1
时间复杂度 O(1)

3.场景三
现在又轮到A不乐意了,A觉得他为了几个数字,却要花销100个内存,于是又和B商量
最后,商量结果为:建立一个索引和数字之间的关系,哈希表就诞生了

哈希表

搞明白了哈希表的结构后,理解它也十分简单,键值对中的key,代表了链表数组中的索引,通过hash算法获取索引,之后只需要O(1)的时间就可以获取到value,当然前提是该索引下的链表元素只有1个,如果存在多个元素,只需要遍历链表,对比key就可以获取到value。
存放元素也是同样道理,通过key获取到数组索引后,判断该索引下的链表是否为空,如果为空,直接存入,否则遍历链表,如果有key相同的,直接替换,没有key相同的放入链表头部

下面是一个简单的带有存放和获取的哈希表

class MyHashMap<K,V>  {
        //数据量
        int size = 0;
        //单链表数组
        HashEntry<K,V>[] hashEntries;
        //数组大小
        int arrayLength;

        public MyHashMap(int size) {
            //初始化数组
            hashEntries = new HashEntry[size];
            arrayLength = hashEntries.length;
        }

        public int size() {
            return size;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean containsKey(K key) {
            return false;
        }

        public V get(K key) {
            //获取hash值
            int hash = Objects.hashCode(key);
            //获取存放索引
            int index = getIndex(hash);

            //判断是否存在值
            if(hashEntries[index] != null){
                HashEntry ptr =  hashEntries[index];

                //轮询链表
                while(ptr != null){
                    if(ptr.hashCode == hash && (key == ptr.key || key.equals(ptr.key))){//判断是否key相同
                        return (V) ptr.value;
                    }
                    ptr = ptr.next;
                }
            }
            return null;
        }

        /**
         * 放入键值对
         *
         * @param key
         * @param value
         * @return
         */
        public V put(K key, V value) {
            //获取hash值
            int hash = Objects.hashCode(key);
            //获取存放索引
            int index = getIndex(hash);

            //判断是否存在值
            if (hashEntries[index] != null) {
                HashEntry ptr = hashEntries[index];
                HashEntry pre = null;

                //轮询链表
                while (ptr != null) {
                    if (ptr.hashCode == hash && (key == ptr.key || key.equals(ptr.key))) {//判断是否key相同
                        //相同,替换
                        HashEntry entry = new HashEntry(key, value, hash);
                        entry.next = ptr.next;
                        if (pre != null) {//前面的元素next指向新的元素
                            pre.next = entry;
                        }
                        return (V) ptr.value;
                    }
                    pre = ptr;
                    ptr = ptr.next;
                }

            }

            //没有key重复的,放在链表头部,或者不存在直接存入
            HashEntry entry = new HashEntry(key, value, hash);
            entry.next = hashEntries[index];
            hashEntries[index] = entry;
            size++;
            
            return null;
        }

        /**
         * hash值转换索引
         * @param hash
         * @return
         */
        private int getIndex(int hash){
            return hash & (arrayLength - 1);
        }

        public V remove(K key) {
            return null;
        }

        public void clear() {

        }

        public Set keySet() {
            return null;
        }

        public Collection values() {
            return null;
        }

        class HashEntry<K,V> {
            K key;
            V value;
            //单链表
            HashEntry next;
            //hash值
            int hashCode;

            public HashEntry(K key, V value, int hashCode) {
                this.key = key;
                this.value = value;
                this.hashCode = hashCode;
            }
        }
    }

对于获取存放我们还可以做个LRU算法优化,使最近操作的都位于链表头

class MyHashMap<K, V> {
        //数据量
        int size = 0;
        //单链表数组
        HashEntry<K, V>[] hashEntries;
        //数组大小
        int arrayLength;

        public MyHashMap(int size) {
            //初始化数组
            hashEntries = new HashEntry[size];
            arrayLength = hashEntries.length;
        }

        public int size() {
            return size;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean containsKey(K key) {
            return false;
        }

        public V get(K key) {
            //获取hash值
            int hash = Objects.hashCode(key);
            //获取存放索引
            int index = getIndex(hash);

            //判断是否存在值
            if (hashEntries[index] != null) {
                HashEntry ptr = hashEntries[index];
                HashEntry pre = null;
                
                //轮询链表
                while (ptr != null) {
                    if (ptr.hashCode == hash && (key == ptr.key || key.equals(ptr.key))) {//判断是否key相同
                        if (pre != null) {
                            //删除之前的元素
                            removeLink(ptr, pre);
                            //放入链表头
                            ptr.next = hashEntries[index];
                            hashEntries[index] = ptr;
                        } else {//处在链表头部了

                        }

                        return (V) ptr.value;
                    }

                    pre = ptr;
                    ptr = ptr.next;
                }
            }
            return null;
        }

        /**
         * 放入键值对
         *
         * @param key
         * @param value
         * @return
         */
        public V put(K key, V value) {
            //获取hash值
            int hash = Objects.hashCode(key);
            //获取存放索引
            int index = getIndex(hash);

            //判断是否存在值
            if (hashEntries[index] != null) {
                HashEntry ptr = hashEntries[index];
                HashEntry pre = null;

                //轮询链表
                while (ptr != null) {
                    if (ptr.hashCode == hash && (key == ptr.key || key.equals(ptr.key))) {//判断是否key相同
                        V oldValue = (V) ptr.value;
                        ptr.value = value;
                       
                        if (pre != null) {
                            //删除之前的元素
                            removeLink(ptr, pre);
                            //放入链表头
                            ptr.next = hashEntries[index];
                            hashEntries[index] = ptr;
                        } else {//处在链表头部了
                            
                        }
                        return oldValue;
                    }
                    pre = ptr;
                    ptr = ptr.next;
                }

            }

            //没有key重复的,放在链表头部,或者不存在直接存入
            HashEntry entry = new HashEntry(key, value, hash);
            entry.next = hashEntries[index];
            hashEntries[index] = entry;
            size++;

            return null;
        }

        /**
         * hash值转换索引
         *
         * @param hash
         * @return
         */
        private int getIndex(int hash) {
            return hash & (arrayLength - 1);
        }

        public V remove(K key) {
            //获取hash值
            int hash = Objects.hashCode(key);
            //获取存放索引
            int index = getIndex(hash);

            //判断是否存在值
            if (hashEntries[index] != null) {
                HashEntry ptr = hashEntries[index];
                HashEntry pre = null;

                //轮询链表
                while (ptr != null) {
                    if (ptr.hashCode == hash && (key == ptr.key || key.equals(ptr.key))) {//判断是否key相同
                        V oldValue = (V) ptr.value;

                        if (pre != null) {
                            //删除之前的元素
                            removeLink(ptr, pre);
                        }else{//没元素了
                            hashEntries[index] = null;
                        }
                        
                        size--;
                        return oldValue;
                    }
                    pre = ptr;
                    ptr = ptr.next;
                }

            }
            
            return null;
        }

        private void removeLink(HashEntry cur, HashEntry pre) {
            if(pre == null || cur == null) return;
            
            pre.next = cur.next;
        }

        public void clear() {

        }

        public Set keySet() {
            return null;
        }

        public Collection values() {
            return null;
        }

        class HashEntry<K, V> {
            K key;
            V value;
            //单链表
            HashEntry next;
            //hash值
            int hashCode;

            public HashEntry(K key, V value, int hashCode) {
                this.key = key;
                this.value = value;
                this.hashCode = hashCode;
            }
        }
    }

简单的哈希表就到这边了,后续还要考虑扩容等功能的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值