自己實現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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章