自己实现HashMap
Published on 2017-11-14 09:32 in 暂未分类 with 是奉壹呀

自己实现HashMap

    一载体

    HashMap是由数组组成,数组元素为哈希链。

    数组

    public class MyHashMap<K, V> {
    
        transient Node<K, V>[] table;
    
    }

    数组元素

    复制代码
    @SuppressWarnings("hiding")
        class Node<K, V> implements Map.Entry<K, V> {
            final int hash;
    
            final K key;
            V value;
    
            Node<K, V> next;
    
            Node(int hash, K key, V value, Node<K, V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
    
            @Override
            public K getKey() {
                return key;
            }
    
            @Override
            public V getValue() {
                return value;
            }
    
            @Override
            public V setValue(V value) {
                V tempValue = value;
                this.value = value;
                return tempValue;
            }
    
            @Override
            public String toString() {
                return "Node [ key=" + key + " , value=" + value + " , " + "]";
            }
    
            @Override
            public int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
    
            public final boolean equals(Object o) {
                if (o == this)
                    return true;
                if (o instanceof Map.Entry) {
                    Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
                    if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                        return true;
                }
                return false;
            }
    
        }
    View Code
    复制代码

     

    二属性

    复制代码
    private static int initCount = 16;//初始化长度
        
        private static float loadFator = 0.75f;//加载因子
        
        private int size;
        
        private static int threshold;//扩容临界值
        
        
        static final int MAXIMUM_CAPACITY = 1 << 30;
    复制代码

     

    添加构造方法初始化属性

    复制代码
    public MyHashMap(){
            this(initCount, loadFator);
        }
        
        public MyHashMap(int initCount){
            this(initCount, loadFator);
        }
        
        public MyHashMap(int initCount, float loadFator){
            if(initCount < 0){
                throw new IllegalArgumentException("初始化长度不合法 : " + initCount);
            }
            
            if(initCount > MAXIMUM_CAPACITY){
                initCount = MAXIMUM_CAPACITY;
            }
            
            if(loadFator <0 || Float.isNaN(loadFator)){
                throw new IllegalArgumentException("加载因子不合法 : " + loadFator);
            }
            
            this.loadFator = loadFator;
            this.threshold = (int) (initCount * 2 * loadFator);
        }
    复制代码

    三方法

    1增加元素

    1.1如果没有哈希碰撞,HashMap就相当于一个数组,而且查找无需遍历。

    复制代码
    public Object put(K key, V value){
            if(table == null || table.length == 0){
                //此时真正创建数组
                table = new Node[initCount];
            }
            int hash = hash(key);
            int len = table.length;
            int index = hash % len;// (len-1) & hash
            if(table[index] == null){
                table[index] = new Node<>(hash, key, value, null);
            }
            
            return null;
        }
    
    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    复制代码

    1.2哈希碰撞的情况。

     如果按jdk8的实现方式,哈希碰撞的情况分为两种情况,首先是形成一个链表,当链表长度大于8时,会分裂成红黑树。这里只按链表实现。

    复制代码
    else{//哈希碰撞
                MyHashMap<K, V>.Node<K, V> e = p;
                
                do{
                    if(e.hash == hash && ((e.key == key) || (key != null && key.equals(e.key)))){
                        V oldValue = p.value;
                        //用新值覆盖旧值
                        p.value = value;
                        return oldValue;
                    }
                    p = e;
                }while((e = p.next) != null);
                //将元素放在链表最前面
                table[index] = new Node<K, V>(hash, key, value, p);
                if(++size > threshold){//扩容
                    resize();
                } 
    复制代码

     

    public void resize(){
            //TODO
        }

     

    2获取元素

    根据key得到数组索引,再判断有无链表。如有,判断链表长度。为1直接返回。大于1,需循环链表。

    复制代码
    public V get(K k){
            int hash = hash(k);
            int len = table.length;
            int index = hash % len;
            Node<K,V> node = table[index];//找到链表
            if (node == null) {
                return null;
            }
            Node<K,V> p;
            if ((p = node.next) == null) {//如果链表只有一个元素,直接返回
                return node.value;
            } else {//如果有多个元素,循环比较key
                p = node;
                do {
                    if (p.key == k || p.key.equals(k)) {
                        return p.value;
                    }
                    node = p;
                } while ((p = node.next) != null);
            }
            return null;
        }
    复制代码

     

    3删除元素

    首先得找到元素。同样是根据key得到数组索引。判断链表是否有值。无值直接返回。有值分两种情况,一种是被删除元素在链表最前面,那么直接将最前面的指针断掉。否则需要将前一个的指针指向后一个元素。由于链表结构只有next,没有前后左右,所以在循环的时候需要随时保存前一个元素,在找到被删除元素的时候,直接将前一个与后一个连接即可。 

    复制代码
    public void remove(K k){
            int hash = hash(k);
            int len = table.length;
            int index = hash % len;
            
            Node<K,V> node = table[index];
            Node<K,V> prev = node;
            while (node != null) {
                if(node.hash == hash && (node.key == k || node.key.equals(k))){
                    if(node == prev){//被删除元素在链表第一位
                        table[index] = node.next;
                    }else{
                        prev.next = node.next;//node 为当前元素  prev为前一个元素  将前一个元素的指针next指向下一个元素
                    }
                    size--;
                }
                prev = node;
                node = node.next;
            };    
            
        }
    复制代码

     

    四扩容

    扩容的触发条件是属性threshold大于HashMap元素个数size。在put元素的时候需要判断。

    在增加,删除的时候,会对元素个数进行增减。

    注意这里的size并不是指数组长度。而是指链表的总长度。

     

    初始化一个HashMap,在put入第一个值的时候,会初始化一个数组,长度为16,算上负载因子,实际使用长度为12。但并不是说这个数组必须要每个索引都有值才会扩容。如下图所示,只有3个索引有值,但3个索引处的链表总长度达到12,也就达到了扩容的临界点。

     

     

    HashMap扩容首先需要将数组扩容。数组长度改变,那么所有元素的链表必须重新组合。不然,查找就会乱套。这是比较耗时的。所以,在使用HashMap的时候,如果能预估长度,最好在初始化的时候指定,避免频繁扩容。

    复制代码
    public void resize(){
            int len = size << 1;
            threshold = (int)(len * loadFator);
            size = 0;
            Node<K,V>[] newTable = new Node[len];
            Node<K,V>[] tempTable = table;
            table = newTable;
            
            int tempSize = tempTable.length;
            for(int i=0; i<tempSize; i++){
                Node<K,V> node = tempTable[i];
                while(node != null){
                    put(node.key, node.value);
                    node = node.next;
                }
            }
        }
    复制代码

     

    五迭代

    1通过keyset来迭代

    keyset得到所有的键的set集合。再通过set的迭代器来迭代。

    首先定义一个set集合。

    Set<K> keySet;

    定义获取keyset的方法

    public Set<K> keySet(){
            return keySet == null ? (keySet = new KeySet()) : null;
        }

    每次keySet为空,需要通过一个内部类KeySet获取。

    复制代码
    class KeySet extends AbstractSet<K>{
    
            @Override
            public Iterator<K> iterator() {
                return new newKeyIterator();
            }
    
            @Override
            public int size() {
                return size;
            }
            
        }
    复制代码

    继续定义newKeyIterator

     

    final class KeyIterator extends HashIterator implements Iterator<K> {
            public final K next() {
                return nextNode().key;
            }
        }

    继续定义KeyIterator 

     

    复制代码
    public class HashIterator{
            
            Node<K,V>[] nodes;
            Node<K,V> prve;
            Node<K,V> next;
            int index;
            
            public HashIterator(){
                nodes = table;
                index = 0;
                prve = next = null;
                do {
                    prve = next = table[index];
                } while((++index < table.length) && next == null);
            }
            
            final Node<K,V> nextNode(){
                Node<K,V> e = next;
                if((next = (prve = e).next) == null && nodes != null){
                    do {
                    } while(index < table.length && (next = nodes[index++]) == null);
                }
                return e;
            }
            
            public boolean hasNext() {
                return next != null;
            }
        }
    复制代码

    再执行一个迭代的时候,先得到一个keys的集合,然后根据集合得到迭代器,这时候会执行HashIterator的构造方法,目的是找到第一个链表。

    Set<String> keys = map.keySet();
    Iterator<String> it = keys.iterator();

     

     然后执行,next时会执行HashIterator的hashNext方法和nextNode方法。

    while(it.hasNext()){
    String key = it.next();
    String value = map.get(key);
    }

     

     2通过entrySet。

    复制代码
    Set<Map.Entry<K,V>> entrySet;
        
        public Set<Map.Entry<K,V>> entrySet(){
            return entrySet == null ? (entrySet = new EntrySet()) : null;
        }
        
        class EntrySet extends AbstractSet<Map.Entry<K,V>>{
    
            @Override
            public Iterator<Map.Entry<K,V>> iterator() {
                return new EntryIterator();
            }
    
            @Override
            public int size() {
                return size;
            }
            
        }
        
        final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
            public final Node<K,V> next() {
                return nextNode();
            }
        }
    复制代码

     

     

     

     

    最终代码 

    复制代码
    import java.util.AbstractSet;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Set;
    import java.util.Iterator;
    
    public class MyHashMap<K, V> {
    
        transient Node<K, V>[] table;
        
        private static int initCount = 16;//初始化长度
        
        private static float loadFator = 0.75f;//加载因子
        
        private int size;
        
        private static int threshold;//扩容临界值
        
        
        static final int MAXIMUM_CAPACITY = 1 << 30;
        
        public MyHashMap(){
            this(initCount, loadFator);
        }
        
        public MyHashMap(int initCount){
            this(initCount, loadFator);
        }
        
        public MyHashMap(int initCount, float loadFator){
            if(initCount < 0){
                throw new IllegalArgumentException("初始化长度不合法 : " + initCount);
            }
            
            if(initCount > MAXIMUM_CAPACITY){
                initCount = MAXIMUM_CAPACITY;
            }
            
            if(loadFator <0 || Float.isNaN(loadFator)){
                throw new IllegalArgumentException("加载因子不合法 : " + loadFator);
            }
            
            this.loadFator = loadFator;
            this.threshold = (int) (initCount * loadFator);
        }
        
        public Object put(K key, V value){
            if(table == null || table.length == 0){
                //此时真正创建数组
                table = new Node[initCount];
            }
            int hash = hash(key);
            int len = table.length;
            int index = (len-1) & hash;
            Node<K,V> p;
            if((p = table[index]) == null){
                table[index] = new Node<K, V>(hash, key, value, null);
            }else{//哈希碰撞
                MyHashMap<K, V>.Node<K, V> e = p;
                Node<K,V> temp = p;
                
                do{
                    if(e.hash == hash && ((e.key == key) || (key != null && key.equals(e.key)))){
                        V oldValue = p.value;
                        //用新值覆盖旧值
                        p.value = value;
                        return oldValue;
                    }
                    temp = e;
                }while((e = temp.next) != null);
                //将元素放在链表最前面
                table[index] = new Node<K, V>(hash, key, value, p);
            }
            
            if(++size > threshold){//扩容
                resize();
            }
            
            return null;
        }
        
        public V get(K k){
            int hash = hash(k);
            int len = table.length;
            int index = (len-1) & hash;
            Node<K,V> node = table[index];//找到链表
            if (node == null) {
                return null;
            }
            Node<K,V> p;
            if ((p = node.next) == null) {//如果链表只有一个元素,直接返回
                return node.value;
            } else {//如果有多个元素,循环比较key
                p = node;
                do {
                    if (p.key == k || p.key.equals(k)) {
                        return p.value;
                    }
                    node = p;
                } while ((p = node.next) != null);
            }
            return null;
        }
        
         
        public void remove(K k){
            int hash = hash(k);
            int len = table.length;
            int index = (len-1) & hash;
            
            Node<K,V> node = table[index];
            Node<K,V> prev = node;
            while (node != null) {
                if(node.hash == hash && (node.key == k || node.key.equals(k))){
                    if(node == prev){//被删除元素在链表第一位
                        table[index] = node.next;
                    }else{
                        prev.next = node.next;//node 为当前元素  prev为前一个元素  将前一个元素的指针next指向下一个元素
                    }
                    size--;
                }
                prev = node;
                node = node.next;
            };    
            
        }
        
        Set<K> keySet;
        
        public Set<K> keySet(){
            return keySet == null ? (keySet = new KeySet()) : null;
        }
        
        Set<Map.Entry<K,V>> entrySet;
        
        public Set<Map.Entry<K,V>> entrySet(){
            return entrySet == null ? (entrySet = new EntrySet()) : null;
        }
        
        class EntrySet extends AbstractSet<Map.Entry<K,V>>{
    
            @Override
            public Iterator<Map.Entry<K,V>> iterator() {
                return new EntryIterator();
            }
    
            @Override
            public int size() {
                return size;
            }
            
        }
        
        final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
            public final Node<K,V> next() {
                return nextNode();
            }
        }
        
        class KeySet extends AbstractSet<K>{
    
            @Override
            public Iterator<K> iterator() {
                return new KeyIterator();
            }
    
            @Override
            public int size() {
                return size;
            }
            
        }
        
        final class KeyIterator extends HashIterator implements Iterator<K> {
            public final K next() {
                return nextNode().key;
            }
        }
        
        
        public class HashIterator{
            
            Node<K,V>[] nodes;
            Node<K,V> prve;
            Node<K,V> next;
            int index;
            
            public HashIterator(){//得到 keys.iterator();的时候执行此构造方法,找到第一个链表
                //找到第一个元素
                nodes = table;
                index = 0;
                prve = next = null;
                do {
                    prve = next = table[index];
                } while((++index < table.length) && next == null);
            }
            
            final Node<K,V> nextNode(){
                Node<K,V> e = next;
                if((next = (prve = e).next) == null && nodes != null){//循环链表
                    do {
                    } while(index < table.length && (next = nodes[index++]) == null);//找到下一个链表
                }
                return e;
            }
            
            public boolean hasNext() {
                return next != null;
            }
        }    
         
         
      
        
        public void resize(){
            int len = size << 1;
            threshold = (int)(len * loadFator);
            size = 0;
            Node<K,V>[] newTable = new Node[len];
            Node<K,V>[] tempTable = table;
            table = newTable;
            
            int tempSize = tempTable.length;
            for(int i=0; i<tempSize; i++){
                Node<K,V> node = tempTable[i];
                while(node != null){
                    put(node.key, node.value);
                    node = node.next;
                }
            }
        }
        
        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
        
    
        @SuppressWarnings("hiding")
        class Node<K, V> implements Map.Entry<K, V> {
            final int hash;
    
            final K key;
            V value;
    
            Node<K, V> next;
    
            Node(int hash, K key, V value, Node<K, V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
    
            @Override
            public K getKey() {
                return key;
            }
    
            @Override
            public V getValue() {
                return value;
            }
    
            @Override
            public V setValue(V value) {
                V tempValue = value;
                this.value = value;
                return tempValue;
            }
    
            @Override
            public String toString() {
                return "[" + key + " : " + value + "]" + " next " + next;
            }
    
            @Override
            public int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
    
            public final boolean equals(Object o) {
                if (o == this)
                    return true;
                if (o instanceof Map.Entry) {
                    Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
                    if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                        return true;
                }
                return false;
            }
    
        }
    
    }
    View Code
    复制代码

     

    posted @   是奉壹呀  阅读(910)  评论(0编辑  收藏  举报
    编辑推荐:
    · 如何编写易于单元测试的代码
    · 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
    · .NET Core 中如何实现缓存的预热?
    · 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
    · AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
    阅读排行:
    · 周边上新:园子的第一款马克杯温暖上架
    · Open-Sora 2.0 重磅开源!
    · .NET周刊【3月第1期 2025-03-02】
    · 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
    · [AI/GPT/综述] AI Agent的设计模式综述
    点击右上角即可分享
    微信分享提示