1.首先拿到TreeMap内所有内部类的的字节码。
- Class[] clazz=tm.getClass().getDeclaredClasses();
- for(Class i:clazz)
- System.out.println(i);
复制代码运行结果:
- class java.util.TreeMap$AscendingSubMap 0
- class java.util.TreeMap$DescendingKeyIterator 1
- class java.util.TreeMap$DescendingSubMap 2
- class java.util.TreeMap$Entry 3
- class java.util.TreeMap$EntryIterator 4
- ........ .....<span style="color: rgb(0, 0, 0); font-family: 宋体; font-size: 10.5pt; text-indent: 21pt; line-height: 1.5;"> </span>
复制代码发现第三个是要找的内部类.
然后
- //拿到TreeMap的root
- Field rootfield=tm.getClass().getDeclaredField("root");
- //拿到TreeMap.Entry的parent和left成员
- Field parentfield=clazz[3].getDeclaredField("parent");
- Field leftfield =clazz[3].getDeclaredField("left");
- //获得TreeMap.Entry的构造方法
- Constructor[] con=clazz[3].getDeclaredConstructors();
复制代码有了这些就好办了。
2.实施暴力访问(名字好恐怖,好暴力).因为这些都是不对外界提供访问,所以要这么做。
- parentfield.setAccessible(true);
- rootfield.setAccessible(true);
- con[0].setAccessible(true);
- leftfield.setAccessible(true);
复制代码3.利用反射过来的构造方法创建一个将要代替原root的Entry对象。这个对象要什么类申明呢??TreeMap.Entry??还是Collection?或者Map??
- Entry newroot=(Entry)con[0].newInstance(new Test(100),100,null);
复制代码没错就用它接收吧!!
TreeMap集合中所有Entry对象的parent都是有指定指向,除了root的指向始终null,所以这里这里要null(也可以指向其他的Entry对象,不过会偷偷跑进集合里去,又取不出来)。
4.进行修改指向(这里有点复杂,每一句都不能少)
- 1>(谋权)让原root的parentfield指向了newroot,虽然已经存在集合里,但是集合是不能访问root的parent属性,所以去取的时候是个null。
- parentfield.set(rootfield.get(tm), newroot);
-
- 2>(造反)设置newroot的left(或regth)指向,既然是根的话左右两边的叉至少要有一个有指向,除非只有根存在。
- leftfield.set(newroot, rootfield.get(tm));
-
- 3>(篡位)将原root的指向改成newroot,也就是篡位,正式宣布newroot已经是新的root了。
- rootfield.set(tm, newroot);
复制代码接下来在打印一下就会发现
System.out.println(tm);
运行结果:
- 10对象-第【1】次和10对象-比较
- 5对象-第【1】次和10对象-比较
- 20对象-第【1】次和10对象-比较
- 1对象-第【1】次和10对象-比较
- 1对象-第【2】次和5对象-比较
- {20=20, 10=10, 5=5, 1=1}
- {20=20, 10=10, 5=5, 1=1, 100=100}
复制代码这个键值对已经成功加入了集合,而且以后再进来键值对,都会先和newroot的键值对比较。
- tm.put(new Test(10), 10);以后再次添加的时候第一次比较的就是100了
- tm.put(new Test(5), 5);
- tm.put(new Test(20), 20);
- tm.put(new Test(1), 1);
复制代码运行结果:
- 10对象-第【1】次和10对象-比较
- 5对象-第【1】次和10对象-比较
- 20对象-第【1】次和10对象-比较 没篡位之前的比较数据
- 1对象-第【1】次和10对象-比较
- 1对象-第【2】次和5对象-比较
- {20=20, 10=10, 5=5, 1=1}
- {20=20, 10=10, 5=5, 1=1, 100=100}
- 10对象-第【1】次和100对象-比较
- 5对象-第【1】次和100对象-比较
- 5对象-第【2】次和10对象-比较 篡位之后再添加的比较数据
- 20对象-第【1】次和10对象-比较
- 20对象-第【2】次和100对象-比较
- 1对象-第【1】次和10对象-比较
- 1对象-第【2】次和5对象-比较
复制代码而且这样还可以跳过TreeMap集合key唯一的特性,存放相同key在集合里,不过那样取出来的话,都是取距离根最近的key所对应Value。(没意义····)
用Map.Entry取出键值对关系的话,应该可以有意义~!~~
总结:
(谋权)让原root的parent指向新root。
只写了(谋权)或者(谋权和造反)时,原root没变,以后进来的元素还是要和原root比较,而且用常规的Key索引不到,隐藏起来了。但会在集合中出现。用关系迭代器可以取出。
(造反)让新root的分叉指向原root。
只写了(造反)时,虽然集合存在指向,但是无法访问到,无法取出新root,只能取出其他元素。
只写了(造反和篡位)时,集合内无法显示,迭代关系也无法显示,但是可以取出,却新root,其他元素虽然在集合显示出来,但是无法取出。(好像病毒一样,看的见,干不掉)
(篡位)让集合接受新root。
只写了(篡位)或者(谋权和篡位)时,就等于把集合里所有元素都删掉,只留下一个新的root在里面。(这招很绝··慎用!!)
搞的好像在当皇帝第一样·~~~~~~~~~~~~~~~~~~