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; // } }