TreeBidiMap實現key和value的互相讀取

package littlehow.commons.demo.collections;

import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.TreeBidiMap;
import org.junit.Test;
/**
 * StructDemo  說明其隱藏的一些數據結構規則以及存放規則
 *
 * @author littlehow
 * @time 2016-06-03 17:18
 */
public class TreeBidiMapDemo {
    /**
     * TreeBidiMap的key和value都要求是Comarable,因爲在node節點插入是樹是根據compareTo方法來確定左右的
     * 其判斷源碼如下:
     * private static final String[] dataName = new String[]{"key", "value"};
     * private static void checkNonNullComparable(Object o, int index) {
     *   if(o == null) {
     *     throw new NullPointerException(dataName[index] + " cannot be null");
     *   } else if(!(o instanceof Comparable)) {
     *     throw new ClassCastException(dataName[index] + " must be Comparable");
     *   }
     *}
     * 可以看出key和value不支持null,並且不支持非Comparable類型
     *
     */

    static class MyKey implements Comparable<MyKey>{
        private String id;
        public MyKey(String id) {
            if (id == null || id.length() == 0)
                throw new IllegalArgumentException("id必須爲非空且長度大於0的字符串");
            this.id = id;
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }

        @Override
        public boolean equals(Object other) {
            if (other == null || !(other instanceof MyKey)) return false;
            MyKey o = (MyKey)other;
            return hashCode() == o.hashCode();
        }

        /**
         * 重寫compare方法
         * @param o
         * @return
         */
        public int compareTo(MyKey o) {
            if (o == null ) return -1;
            return this.id.compareTo(o.id);
        }

        @Override
        public String toString() {
            return "mykey-" + id;
        }
    }
    @Test
    public void testBidimap() {
        /** 適合場景爲通過key想獲取value和通過value想獲取key
         * 若果不同的key放置了相同的value,那麼前一個key會被後一個key覆蓋
         * */
        BidiMap map = new TreeBidiMap();
        map.put("color wolf", "色狼");
        map.put("壞人", "good man");
        map.put("好人", "good man");
        /** 根據key獲取value,和普通map沒區別 */
        System.out.println(map.get("color wolf"));//色狼
        System.out.println(map.getKey("good man"));//好人
        /**  查看其組成 */
        System.out.println(map);//{color wolf=色狼, 好人=good man}
        /** 反轉map功能 */
        BidiMap inverMap = map.inverseBidiMap();
        System.out.println(inverMap);//{good man=好人, 色狼=color wolf}
        /** contains方法也提供了雙向的 */
        System.out.println(map.containsKey("color wolf"));//true
        System.out.println(map.containsValue("good man"));//true
        /***
         * TreeBidiMap的key和value雖然沒有泛型,但是其值必須爲同一種類型
         * 如果new MyKey("littlehow")放入map中,則會拋出類型轉換異常
         * 所以必須新new一個TreeBidiMap對象
         *
         * 具體原因我們來看看源碼:
         *    //put方法入口
         *    public Object put(Object key, Object value) {
         *       return this.doPut((Comparable)key, (Comparable)value, 0);
         *    }
         *    private Object doPut(Comparable key, Comparable value, int index) {
         *            checkKeyAndValue(key, value);
         *            Object prev = index == 0?this.doGet(key, 0):this.doGet(value, 1);
         *            ...省略很多代碼,從上面可以看出會進入doGet方法
         *    }
         *
         *    //可以看出doget方法會進行一次lookup
         *    private Object doGet(Comparable obj, int index) {
         *          checkNonNullComparable(obj, index);
         *          TreeBidiMap.Node node = this.lookup(obj, index);
         *          return node == null?null:node.getData(oppositeIndex(index));
         *   }
         *
         *  //可以看出這裏進行了一次compare比較,
         *  //在compareTo時發現類型不一致,這時候就拋出了類型轉換異常
         *  private TreeBidiMap.Node lookup(Comparable data, int index) {
         *     ...省略代碼
         *     cmp = compare(data, node.getData(index));
         *     ...省略代碼
         *  }
         */
        BidiMap mymap = new TreeBidiMap();
        mymap.put(new MyKey("littlehow"), "little how");
        System.out.println(mymap);//{mykey-littlehow=little how}
    }

    //部分源碼,從代碼中可以看出TreeBidiMap維護的是一個長度爲2的node數組
    //node節點中存放Comparable[] data存放數據
    //在node初始化時左節點、右節點和父節點都申明兩個長度爲2的node節點
    //當有一個新節點進入時,它會循環首先用compareTo方法對比,判斷出數據應該在其對比的左節點還是右節點
    //這樣就保存了一個Comparable數據的二叉樹結構,因爲每個節點都是一個長度爲2的compare
    //data[0]表示key,data[1]表示value,這樣就可以隨時用key獲取value也可以用value獲取key
    //如果想要深入研究其結構,可以閱讀其源碼...
    //分享就到這裏吧,本想剖析其put和get方法,但因爲時間有限,在這裏就不說了
    //如果以後有時間,將繼續深入解析其數據結構
//    private TreeBidiMap.Node[] rootNode = new TreeBidiMap.Node[2];
//    static class Node implements Entry, KeyValue {
//    private Comparable[] data;
//    private TreeBidiMap.Node[] leftNode;
//    private TreeBidiMap.Node[] rightNode;
//    private TreeBidiMap.Node[] parentNode;
//    private boolean[] blackColor;
//    private int hashcodeValue;
//    private boolean calculatedHashCode;
//
//    Node(Comparable key, Comparable value) {
//        this.data = new Comparable[]{key, value};
//        this.leftNode = new TreeBidiMap.Node[2];
//        this.rightNode = new TreeBidiMap.Node[2];
//        this.parentNode = new TreeBidiMap.Node[2];
//        this.blackColor = new boolean[]{true, true};
//        this.calculatedHashCode = false;
//    }
}
發佈了55 篇原創文章 · 獲贊 75 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章