TreeMap學習記錄

根據一致性Hash算法學習TreeMap

0、簡介

​ 本次根據使用TreeMap實現簡單一致性Hash算法的例子來學習下這個讓人忘了學學了忘的TreeMap源碼,希望從中學到它的設計思想。本次主要記錄以下幾個點。

​ 1、TreeMap實現一致性Hash算法示例

​ 2、TreeMap構造函數及屬性

​ 3、TreeMap核心方法分析

1、TreeMap實現一致性Hash算法示例

​ 簡介下算法內容,先構造一個長度爲Integer.MAX_VALUE長度的一個整數環(一致性Hash環),根據節點的Hash值將服務器放在Hash環上。然後根據數據的Key值計算其Hash值,接着在Hash環上順序查找距離這個Key值最近的服務器節點,完成Key到服務器的映射。

​ 由於java本身的hashCode值分散不夠均勻,所以使用FNV1_32_HASH算法替換java對象自身的hashCode方法來計算key的hash值。

​ 下面先看下一個使用TreeMap實現一致性Hash算法的示例。

import java.util.SortedMap;
import java.util.TreeMap;

public class ConsistentHashing {

    private static String [] servers = {"127.0.0.1:9002","127.0.0.1:9003","127.0.0.1:9004"};

    private static TreeMap<Integer,String> serverList = new TreeMap<>();

    static {
        for(int i=0;i<servers.length;i++){
            int hash = getHash(servers[i]);
            serverList.put(hash,servers[i]);
        }
    }

    private static int getHash(String str) {
        final int p = 16777619;

        int hash = (int) 2166136261L;

        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.charAt(i)) * p;

        hash += hash << 13;

        hash ^= hash >> 7;

        hash += hash << 3;

        hash ^= hash >> 17;

        hash += hash << 5;

        if (hash < 0)
            hash = Math.abs(hash);

        return hash;
    }

    private static String getServer(String node){
        int hash = getHash(node);
        SortedMap<Integer,String> subMap = serverList.tailMap(hash);

        if(subMap == null || subMap.size()<=0){
            subMap = serverList;
        }
        return subMap.get(subMap.firstKey());
    }

    public static void main(String[] args) {
        String [] nodes = { "127.0.0.1:1111", "221.226.0.1:2222"};

        for(String node : nodes){
            System.out.println("node hash:"+getHash(node)+"路由服務器:"+getServer(node));
        }
    }
}

​ 以上是一個簡易的一致性Hash算法,其中用到了TreeMap一些特性,下面我們開始分析TreeMap。

2、TreeMap構造函數及屬性

構造函數

// 無參構造函數
public TreeMap() {
        comparator = null;
}

// 帶Comparator的構造函數
public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
}

// 帶其他Map的構造函數
public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
}

// 帶其他排序Map的構造函數
public TreeMap(SortedMap<K, ? extends V> m) {
        comparator = m.comparator();
        try {
            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
}

屬性

// 比較器,用於比較key鍵
private final Comparator<? super K> comparator;
// 紅黑樹的根節點
private transient Entry<K,V> root;
// 元素個數
private transient int size = 0;
// 修改次數
private transient int modCount = 0;
// TreeMap內部紅黑樹節點
static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;
}

類繼承結構

在這裏插入圖片描述

3、TreeMap核心方法分析

​ 按照慣例分析下TreeMap的常用核心方法。

put(K key, V value)

​ 由下面分析可以得出TreeMap對元素數量沒有限制,可以無限添加。

public V put(K key, V value) {
    	// 獲取紅黑樹根節點
        Entry<K,V> t = root;
    	// 如果根節點爲空則創建根節點並返回null
        if (t == null) {
            compare(key, key); // type (and possibly null) check
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // 獲取當前的比較器
        Comparator<? super K> cpr = comparator;
    	// 如果比較器存在
        if (cpr != null) {
            do {
                // 獲取紅黑樹根節點
                parent = t;
                // 然後根據比較結果定位元素位置
                // 若元素key已經存在,則更新其值,要不然結束循環
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            // 如果比較器不存在,且key爲空則拋出空指針異常
            if (key == null)
                throw new NullPointerException();
            // 這塊要求若沒有傳入比較器,那麼key必須是實現Comparable接口,要不然出錯
            @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                // 定位key所在內容
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
    
    	// 如果上面都沒定位到,則創建一個新的節點放入key\value值,並設置父節點
        Entry<K,V> e = new Entry<>(key, value, parent);
    	// 按照查找樹的性質,若給定key比父節點小,則是父節點的左子節點
    	// 若給定key比父節點key大,則放入父節點的右子節點
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
    	// 在插入節點後需要調用調整紅黑樹結構的方法
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

get(Object key)

public V get(Object key) {
        Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
        // 判斷是否有比較器,若有則調用另一個方法獲取指定key的值
        if (comparator != null)
            return getEntryUsingComparator(key);
    	// 若沒有比較器則key值不能爲空,爲空則拋出空指針異常
        if (key == null)
            throw new NullPointerException();
        // 下面key若未實現Comparable接口則會拋出異常
    	@SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
    	// 下面就按照二插搜索樹的方法來查找元素
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
}

// 有比較器時候調用的查找元素方法
final Entry<K,V> getEntryUsingComparator(Object key) {
        @SuppressWarnings("unchecked")
        K k = (K) key;
    	// 獲取比較器
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            // 按照二插搜索樹的方式查找元素
            Entry<K,V> p = root;
            while (p != null) {
                int cmp = cpr.compare(k, p.key);
                if (cmp < 0)
                    p = p.left;
                else if (cmp > 0)
                    p = p.right;
                else
                    return p;
            }
        }
        return null;
}

remove(Object key)

// 移除指定元素
public V remove(Object key) {
    	// 獲取指定的Entry,方法如上面分析所示
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;
	    // 如果指定元素存在,則在刪除元素後返回其舊值
        V oldValue = p.value;
    	// 調用此方法函數指定元素
        deleteEntry(p);
        return oldValue;
}


private void deleteEntry(Entry<K,V> p) {
        modCount++;
        size--;

        // If strictly internal, copy successor's element to p and then make p
        // point to successor.
        if (p.left != null && p.right != null) {
            Entry<K,V> s = successor(p);
            p.key = s.key;
            p.value = s.value;
            p = s;
        } // p has 2 children

        // Start fixup at replacement node, if it exists.
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) {
            // Link replacement to parent
            replacement.parent = p.parent;
            if (p.parent == null)
                root = replacement;
            else if (p == p.parent.left)
                p.parent.left  = replacement;
            else
                p.parent.right = replacement;

            // Null out links so they are OK to use by fixAfterDeletion.
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { // return if we are the only node.
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            if (p.color == BLACK)
                fixAfterDeletion(p);

            if (p.parent != null) {
                if (p == p.parent.left)
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null;
            }
        }
}

未完待續。。。

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