微信扫一扫

028-83195727 , 15928970361
business@forhy.com

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