Java - 第九章手工实现HashMap中未讲的扩容问题

 HashMap的位桶数组实际使用时,大小是可变的。如果位桶数组中的元素达到(0.75*数组 length), 就重新调整数组大小变为原来2倍大小。

未扩容时的代码如下:

/**
 * 新建一个节点
 * put方法增加键值对,键相同时覆盖相应节点,不同时放在最后
 * 在table同一个地址的next增加值
 * 重写toString方法
 * get的方法
 */

package mypro09;

public class SxtHashMap03 {
    Node2[] table; //位桶数组。bucket array
    int size; //存放键值对的个数
    
    public SxtHashMap03(){
        table = new Node2[16]; //长度一般定义为2的整数幂
    }
    
    public Object get(Object key) {
        
        int hash = myHash(key.hashCode(), table.length);
        Object value = null;
        
        if(table[hash]!=null) {
            Node2 temp = table[hash];
            
            while(temp!=null) {
                if(temp.key.equals(key)) { //如果相等,则说明了找到键值对,返回相应的value
                    value = temp.value;
                    break;
                }else {
                    temp = temp.next;
                }
            }
        }
        
        return value;
    }
    
    public void put(Object key, Object value) {
        
        //如果要完善。还需要考虑数组扩容问题!!!见ArrayList!!!!
        
        
        //定义了新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;
        
        Node2 temp = table[newNode.hash];
        
        Node2 iterLast = null; //正在遍历的最后一个元素,为保存遍历的最后一个节点信息
        boolean keyRepeat = false; //在循环外判断循环到最后一个也没有找到重复key的情况
        if(temp==null) {
            //此处数组元素为空,则直接将新节点放进去
            table[newNode.hash] = newNode;
        }else {
            //此处数组元素不为空,遍历对应链表
            while(temp!=null) {
                
                //判断key如果重复,则覆盖
                if(temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value; 
                    break;
                }else {
                    //如果key不重复,则遍历下一个
                    iterLast = temp;
                    temp = temp.next;
                }
            }
            if(!keyRepeat) { //没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;
            }
        }
    }
    
    @Override
    public String toString() {
        //[10:aa, 20:bb]
        StringBuilder sb = new StringBuilder("[");
        
        //遍历bucket数组
        for(int i=0;i<table.length;i++) {
            Node2 temp = table[i];
            
            //遍历链表
            while(temp!=null) {
                sb.append(temp.key+":"+temp.value+",");
                temp = temp.next;
            }
        }
        sb.setCharAt(sb.length()-1, ']');
        return sb.toString();
    }
    
    //用v=key.hashCode,求出哈希值
    public static int myHash(int v, int length) {
//        System.out.println("hash in myHash" + (v&(length-1))); //直接位运算,效率较高
//        System.out.println("hash in myHash" + (v%(length))); //直接取模运算,效率较低
        return v&(length-1);
    }
    
    public static void main(String[] args) {
        SxtHashMap03 map = new SxtHashMap03();
        map.put(10, "aa");
        map.put(20, "bb");
        map.put(30, "cc");
        map.put(30, "22c");
        
        map.put(53, "gg");
        map.put(69, "ff");
        map.put(85, "shua");
        
        System.out.println(map);
        
        System.out.println(map.get(53));
//        for(int i=10;i<100;i++) {
//            System.out.println(i+"---"+myHash(i, 16)); //53,69,85
//        }
        
    }
}

对put函数增加扩容功能,为方便验证算法正确此处将table的length改为了4

修改后如下:

    int tNodeCount = 0; //存放位桶数组元素个数
    public void put(Object key, Object value) {
        
        //如果要完善。还需要考虑数组扩容问题!!!见ArrayList!!!!
        
        
        //定义了新的节点对象
        Node2 newNode = new Node2();
        newNode.hash = myHash(key.hashCode(), table.length);
        newNode.key = key;
        newNode.value = value;
        newNode.next = null;
        
        Node2 temp = table[newNode.hash];
        
        Node2 iterLast = null; //正在遍历的最后一个元素,为保存遍历的最后一个节点信息
        boolean keyRepeat = false; //在循环外判断循环到最后一个也没有找到重复key的情况
        
        
        if(temp==null) {
            //此处数组元素为空,则直接将新节点放进去
            tNodeCount++;
            table[newNode.hash] = newNode;
            //什么时候扩容
            if(tNodeCount==0.75*table.length) {
                
                //怎么扩容,如果位桶数组中的元素达到(0.75*数组 length),就重新调整数组大小变为原来2倍大小。
                Node2[] newTable = new Node2[table.length<<1]; //4-->4*2
                System.arraycopy(table, 0, newTable, 0, table.length);
                table = newTable;
            }
            
        }else {
            //此处数组元素不为空,遍历对应链表
            while(temp!=null) {
                
                //判断key如果重复,则覆盖
                if(temp.key.equals(key)) {
                    keyRepeat = true;
                    temp.value = value; 
                    break;
                }else {
                    //如果key不重复,则遍历下一个
                    iterLast = temp;
                    temp = temp.next;
                }
            }
            if(!keyRepeat) { //没有发生key重复的情况,则添加到链表最后
                iterLast.next = newNode;
            }
            
        }
    }
    

利用断点,可以看到原本table.length = 4,现在改为了table.length = 8

猜你喜欢

转载自www.cnblogs.com/kl-1998/p/10804609.html
今日推荐