TreeBidiMap实现key和value的互相读取
2016-06-17
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; // } }