一遍文章了解Java集合框架TreeMap实现原理
本文大纲:
1.分析Java TreeMap源码了解其实现原理
2.根据Java TreeMap实现原理自定义实现MyTreeMap
基本概念:
TreeMap基于红黑树数据结构实现
Entry就是树的节点node
红黑树数据结构特点:
1.从根节点开始,左边小,右边大
2.从整个树来看,最左边的子节点最小,最右边的子节点最大
数据结构示意图:
Entry 实体属性:
K key; //键V value;//值Entry<K,V> left;//左边节点引用Entry<K,V> right;//右边节点引用Entry<K,V> parent;//父节点引用boolean color = BLACK;//节点类型 红色/黑色
put 元素流程:
判断有没有根节点,如果没有就先创建根节点
获取比较器 有比较器则使用比较器判断大小,没有则使用 key的比较规则
根据比较结果 从根节点,根据 key 与 每个节点的key 进行比较,小于从左边搜索 ,大于从右边搜索 直到没有子节点
创建Entry对象 初始化key value 以找到的节点为parent 并且根据最后的比较结果判断放left 还是 right
对树进行平衡优化
public V put(K key, V value) {Entry<K,V> t = root;//1.判断有没有根节点,如果没有就先创建跟节点if (t == null) {compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;// split comparator and comparable paths//2.获取排序器,有比较规则使用排序器判断大小,没有则使用 key的比较规则//直接使用new TreeMap 创建对象排序器为空//会取key作为比较,而相对于String类型 默认是ASCII进行比较Comparator super K> cpr = comparator;if (cpr != null) {//3.根据排序器 查找放到哪个树节点下面,do {parent = t;cmp = cpr.compare(key, t.key);//4.从根节点,根据key 与 节点key进行比较,小的从左边搜索 ,大的从右边搜索 直到没有子节点if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else//如果两个key相等 则覆盖原来key的值return t.setValue(value);} while (t != null);}else {if (key == null)throw new NullPointerException();//获取key的比较规则 key必须继承Comparable 并且实现compareTo方法@SuppressWarnings("unchecked")Comparable super K> k = (Comparable super K>) key;//找到当前key合适的父节点do {parent = t;cmp = k.compareTo(t.key);//从根节点,根据key 与 节点key进行比较,小的从左边搜索 ,大的从右边搜索 直到没有子节点if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else//相同则覆盖当前值 并且返回return t.setValue(value);} while (t != null);}//创建EntryEntry<K,V> e = new Entry<>(key, value, parent);//根据最后的比较结果得出放节点左边还是右边if (cmp < 0)parent.left = e;elseparent.right = e;//对树结构进行平衡优化fixAfterInsertion(e);size++;modCount++;return null;}
get元素流程:
判断是否使用了比较器
使用了比较器规则根据比较器判断搜索方向
如果 key 与节点 key 相等则返回结果
public V get(Object key) {Entry<K,V> p = getEntry(key);return (p==null ? null : p.value);}final Entry<K,V> getEntry(Object key) {//判断是否存在比较器if (comparator != null)//根据比较器找到Entryreturn getEntryUsingComparator(key);//key==null 抛异常if (key == null)throw new NullPointerException();//获取比较器Comparable<? super K> k = (Comparable<? super K>) key;//获取根节点Entry<K,V> p = root;//遍历树 直到没有子节点或者两个key相同就返回entry实体while (p != null) {//拿key 和 节点key 进行比较 得出从哪边进行查找int cmp = k.compareTo(p.key);if (cmp < 0)//从左边寻找p = p.left;else if (cmp > 0)//从右边寻找p = p.right;else//相同直接返回return p;}return null;}//根据比较器获取final Entry<K,V> getEntryUsingComparator(Object key) {K k = (K) key;//获取比较器Comparator<? super K> cpr = comparator;//判断表器不为空if (cpr != null) {//从root节点开始Entry<K,V> p = root;while (p != null) {//比较大小 判断从那边进行搜索int cmp = cpr.compare(k, p.key);if (cmp < 0)p = p.left;else if (cmp > 0)p = p.right;else//搜到到直接返回return p;}}return null;}
迭代算法:
next获取流程:
1.找到数的最左边,就是最小值
2.判断是否有右边节点,有则切换到最左边,否则判断是否有父节点
3.有父节点,并且当前节点是父节点的右边(在当前父子节点下一已经是最大的了),则切到父节点的父节点
4.返回当前节点(不是next值 是当前值)
以entrySet方法为例:
public Set<Map.Entry<K,V>> entrySet() {EntrySet es = entrySet;return (es != null) ? es : (entrySet = new EntrySet());}class EntrySet extends AbstractSet<Map.Entry<K,V>> {public Iterator<Map.Entry<K,V>> iterator() {//迭代器实现方法 实例化迭代器//getFirstEntry() 获取数最左边的元素return new EntryIterator(getFirstEntry());}}//获取最左边的元素 也就是最左边的元素final Entry<K,V> getFirstEntry() {Entry<K,V> p = root;if (p != null)while (p.left != null)p = p.left;return p;}final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {EntryIterator(Entry<K,V> first) {super(first);}public Map.Entry<K,V> next() {//调用父类nextEntryreturn nextEntry();}}//PrivateEntryIterator父类nextEntry实现PrivateEntryIterator(Entry<K,V> first) {expectedModCount = modCount;lastReturned = null;//设置初始第一节点next = first;}final Entry<K,V> nextEntry() {Entry<K,V> e = next;if (e == null)throw new NoSuchElementException();//判断修改次数是否一致if (modCount != expectedModCount)throw new ConcurrentModificationException();//选择元素next = successor(e);lastReturned = e;//返回当前节点return e;}//从最左边搜索元素 从大到小一次排列static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {if (t == null)return null;else if (t.right != null) {//判断节点右边是否有值Entry<K,V> p = t.right;while (p.left != null)//循环切换到最左边的值p = p.left;//返回从右边找到的下一个值return p;} else {//右边节点没有从父节点找Entry<K,V> p = t.parent;Entry<K,V> ch = t;//如果是当前节点在父节点的右边,则获取到父节点的父节点while (p != null && ch == p.right) {ch = p;p = p.parent;}return p;}}
根据上述结构原理,自己实现TreeMap
为了简洁易懂 主要实现如下三个方法:
put
get
entrySet
自定义具体实现如下:
package com.kexun;import java.util.*;public class MyTreeMap<K, V> {private MyEntry<K, V> root;private int size = 0;private int modCount = 0;private MyEntrySet entrySet;public V put(K key, V value) {MyEntry<K, V> t = root;if (t == null) {//初始化跟节点root = new MyEntry<>(key, value, null);size = 1;modCount++;return null;}//key不能为空if (key == null) {throw new NullPointerException();}//这里只使用key的比较器Comparable<? super K> k = (Comparable<? super K>) key;//存放找到的放元素的节点MyEntry<K, V> parent;//比较结果int cmp;//寻找子节点存放元素do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0) {t = t.left;} else if (cmp > 0) {t = t.right;} else {//相同接替换掉return t.setValue(value);}} while (t != null);MyEntry<K, V> e = new MyEntry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;//优化平衡树结构fixAfterInsertion(e);size++;modCount++;return null;}public V get(Object key) {MyEntry<K, V> entry = getEntry(key);if (entry != null) {return entry.value;}return null;}private MyEntry<K, V> getEntry(Object key) {if (key == null) {throw new NullPointerException();}Comparable<? super K> k = (Comparable<? super K>) key;//从跟节点开始寻找MyEntry<K, V> p = root;while (p != null) {//根据红黑树特性 选择是从左边还是右边寻找int cmp = k.compareTo(p.key);if (cmp < 0) {p = p.left;} else if (cmp > 0) {p = p.right;} else {//如果相等返回return p;}}return null;}public MyEntrySet entrySet() {//缓存 避免多次初始化MyEntrySet es = entrySet;return (es != null) ? es : (entrySet = new MyEntrySet());}//自定义entryclass MyEntrySet {public MyEntryIterator<MyEntry<K, V>> iterator() {return new MyEntryIterator(getFirstEntry());}public int size() {return MyTreeMap.this.size;}}//获取红黑数最左边 也就是最小的元素private MyEntry<K, V> getFirstEntry() {MyEntry<K, V> p = root;if (p != null)while (p.left != null)p = p.left;return p;}//自定义迭代器class MyEntryIterator<T> {MyEntry<K, V> next;MyEntry<K, V> lastReturned;int expectedModCount;MyEntryIterator(MyEntry<K, V> first) {expectedModCount = modCount;lastReturned = null;next = first;}//获取下一个元素public MyEntry<K, V> next() {return nextEntry();}//是否存在下一个public boolean hasNext() {return next != null;}//获取下一个元素public MyEntry<K, V> nextEntry() {MyEntry<K, V> e = next;if (e == null)throw new NoSuchElementException();if (modCount != expectedModCount)throw new ConcurrentModificationException();//具体获取方法next = successor(e);lastReturned = e;return e;}//找出下一个元素private <K, V> MyEntry<K, V> successor(MyEntry<K, V> t) {if (t == null)return null;else if (t.right != null) {MyEntry<K, V> p = t.right;while (p.left != null)p = p.left;return p;} else {MyEntry<K, V> p = t.parent;MyEntry<K, V> ch = t;while (p != null && ch == p.right) {ch = p;p = p.parent;}return p;}}}//自定义Entry数节点对象static final class MyEntry<K, V> {K key;V value;MyEntry<K, V> left;MyEntry<K, V> right;MyEntry<K, V> parent;boolean color = BLACK;MyEntry(K key, V value, MyEntry<K, V> parent) {this.key = key;this.value = value;this.parent = parent;}public K getKey() {return key;}public V getValue() {return value;}public V setValue(V value) {V oldValue = this.value;this.value = value;return oldValue;}public String toString() {return "key:" + key + " value:" + value;}}private static final boolean RED = false;private static final boolean BLACK = true;/*下面是红黑树实现算法 可忽略 我也是从Jdk源码复制的*/private static <K, V> boolean colorOf(MyEntry<K, V> p) {return (p == null ? BLACK : p.color);}private static <K, V> MyEntry<K, V> parentOf(MyEntry<K, V> p) {return (p == null ? null : p.parent);}private static <K, V> void setColor(MyEntry<K, V> p, boolean c) {if (p != null)p.color = c;}private static <K, V> MyEntry<K, V> leftOf(MyEntry<K, V> p) {return (p == null) ? null : p.left;}private static <K, V> MyEntry<K, V> rightOf(MyEntry<K, V> p) {return (p == null) ? null : p.right;}/*** From CLR*/private void rotateLeft(MyEntry<K, V> p) {if (p != null) {MyEntry<K, V> r = p.right;p.right = r.left;if (r.left != null)r.left.parent = p;r.parent = p.parent;if (p.parent == null)root = r;else if (p.parent.left == p)p.parent.left = r;elsep.parent.right = r;r.left = p;p.parent = r;}}/*** From CLR*/private void rotateRight(MyEntry<K, V> p) {if (p != null) {MyEntry<K, V> l = p.left;p.left = l.right;if (l.right != null) l.right.parent = p;l.parent = p.parent;if (p.parent == null)root = l;else if (p.parent.right == p)p.parent.right = l;else p.parent.left = l;l.right = p;p.parent = l;}}/*** From CLR*/private void fixAfterInsertion(MyEntry<K, V> x) {x.color = RED;while (x != null && x != root && x.parent.color == RED) {if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {MyEntry<K, V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == rightOf(parentOf(x))) {x = parentOf(x);rotateLeft(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateRight(parentOf(parentOf(x)));}} else {MyEntry<K, V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == leftOf(parentOf(x))) {x = parentOf(x);rotateRight(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateLeft(parentOf(parentOf(x)));}}}root.color = BLACK;}}
以上就是本期内容,感谢大家的阅读
