JDK之TreeMap源码解析
2017-03-01 09:55
295 查看
刚入java不久的程序猿,对于简单的使用已毫不满足,最终为了一探究竟,翻开了JDK的源码,以下观点为自己的理解及看了多篇博客的总结,欢迎各位大神指出不对的地方,当然也欢迎和我一样刚学的同学,一起加油努力吧~~
TreeMap看名字就知道是Map家族的成员,通过key,value键值对的方式存储元素,相对于其他map,TreeMap是有序的,TreeMap的底层是由红黑树实现的,藉由LZ算法不精,红黑树相对也比较复杂,所以参考了很多资料,浅谈一下源码的剖析
在看源码之前,首先先大致说下树的概念。刚刚上面也说了TreeMap底层是由红黑树实现的,所以这里我们得对红黑树有个大致印象再看源码。树无非就是由根生枝生叶的一个结构,就像java里的类也是有根Object一层一层下来的,这就是普通的树,这里我们把根叫做根节点,这里有一个规则就是根节点数据会大于左子节点,小于右子节点,但是由于会一边倒,所以有了后期优化的平衡二叉树和红黑树等等,这里我们着重介绍下红黑树。
有了树的概念,下面来简单讲一下红黑树是什么,由于根节点为黑色,所以其子节点为红色,这个红色节点的子节点为黑色,依次延伸,红黑树会保持两边平衡,从任意一节点到叶子所有路径都会包含相同数量的黑色节点,红黑树会通过左旋右旋着色来重新进行结构的调整保持平衡。
简单介绍后,我们来深入一下源码
好了简单说一下,继承AbstractMap,实现了3个接口,第一个接口过会着重讲下,后两个接口应该都不陌生了,克隆方法与序列化,下面专门把那个比较陌生的接口拿出来看看到底是干什么的,NavigableMap这个接口继承了SortedMap,所以我们先看下SortedMap接口里定义了哪些方法
好了,看了上面的接口,我们来看下NavigableMap这个接口里新增的方法
看了一下接口的方法,感觉用处很相似,返回的都是key值排序后其中的key或entry,这边也没太多说的,知道每个方法干什么的就行,下面开始正式的看TreeMap的源码,首先我们来了解一下很重要的一个内部类
上面的entry很重要,毕竟是由一个个节点构成的,所以我们要了解它的组成,下面看一下TreeMap的全局变量
TreeMap的全局变量就这些,也都比较简单,comparator一个用来排序的,TreeMap之所以有序就是这个的作用了,其他的都比较简单,下面我们继续看构造函数
上面是TreeMap的几个构造方法,开篇说了由于红黑树相对比较复杂,所以这里我就挑一些常用的方法进行学习理解,如果有感兴趣的同学可以翻开源码深钻一下,应该可以收获很多,下面我们来看看一些常用的方法。
上面介绍了get的时候,TreeMap内部是如何去操作的,接下来看看put
在这里将右旋方法贴出,与左旋类似就不详细说明了,相信大家应该都能看懂。到这里基本上最常用的方法基本已经介绍完了,对于算法不好的LZ表示头已经快绕晕了,下面列几个简单点也能用到的方法结束TreeMap的解析
好了,没有太深入的了解TreeMap,但是主要分析了几个常用方法的实现原理,如果有感兴趣想深入的同学,可以自己看看源码,相信对算法有兴趣的朋友应该还是会喜欢的
TreeMap简述 |
TreeMap源码解析 |
有了树的概念,下面来简单讲一下红黑树是什么,由于根节点为黑色,所以其子节点为红色,这个红色节点的子节点为黑色,依次延伸,红黑树会保持两边平衡,从任意一节点到叶子所有路径都会包含相同数量的黑色节点,红黑树会通过左旋右旋着色来重新进行结构的调整保持平衡。
简单介绍后,我们来深入一下源码
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ ... }
好了简单说一下,继承AbstractMap,实现了3个接口,第一个接口过会着重讲下,后两个接口应该都不陌生了,克隆方法与序列化,下面专门把那个比较陌生的接口拿出来看看到底是干什么的,NavigableMap这个接口继承了SortedMap,所以我们先看下SortedMap接口里定义了哪些方法
public interface SortedMap<K,V> extends Map<K,V> { /** * 根据key进行排序的方法 */ Comparator<? super K> comparator(); /** * 将fromKey到toKey进行排序 */ SortedMap<K,V> subMap(K fromKey, K toKey); /** * 将小于toKey的key进行排序 */ SortedMap<K,V> headMap(K toKey); /** * 将大于fromKey的key进行排序 */ SortedMap<K,V> tailMap(K fromKey); /** * 返回第一个key值 */ K firstKey(); /** * 返回最后一个key值 */ K lastKey(); /** * 获得所有的key */ Set<K> keySet(); /** * 获得所有的value */ Collection<V> values(); /** * 将key,value转换为Set类型 */ Set<Map.Entry<K, V>> entrySet(); }
好了,看了上面的接口,我们来看下NavigableMap这个接口里新增的方法
public interface NavigableMap<K,V> extends SortedMap<K,V> { /** * 返回最大key值且key值小于参数key的entry */ Map.Entry<K,V> lowerEntry(K key); /** * 返回最大key值且key值小于参数key */ K lowerKey(K key); /** * 返回最大key值且key值 <= 参数key的entry */ Map.Entry<K,V> floorEntry(K key); /** * 返回最大key值且key值 <= 参数key的key值 */ K floorKey(K key); /** * 返回最小key值且key值 >= 参数key的entry */ Map.Entry<K,V> ceilingEntry(K key); /** * 返回最小key值且key值 >= 参数key的key值 */ K ceilingKey(K key); /** * 返回最小key值且key值 > 参数key的entry */ Map.Entry<K,V> higherEntry(K key); /** * 返回最小key值且key值 > 参数key的key值 */ K higherKey(K key); /** * 返回最小key值的entry */ Map.Entry<K,V> firstEntry(); /** * 返回最大key值的entry */ Map.Entry<K,V> lastEntry(); /** * 移除最小key值的entry */ Map.Entry<K,V> pollFirstEntry(); /** * 移除最大key值的entry */ Map.Entry<K,V> pollLastEntry(); /** * 返回一个顺序反转后的Map */ NavigableMap<K,V> descendingMap(); /** * 返回包含所有key的set集合,正常排序 */ NavigableSet<K> navigableKeySet(); /** * 返回包含所有key的set集合,顺序为反转排序 */ NavigableSet<K> descendingKeySet(); /** * 返回key值在fromKey到toKey之间的Map */ NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive); /** * 返回key值 <= toKey的Map */ NavigableMap<K,V> headMap(K toKey, boolean inclusive); /** * 返回key值 >= fromKey的Map */ NavigableMap<K,V> tailMap(K fromKey, boolean inclusive); /** * 等同于subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) */ SortedMap<K,V> subMap(K fromKey, K toKey); /** * 等同于headMap(K toKey, boolean inclusive) */ SortedMap<K,V> headMap(K toKey); /** * 等同于tailMap(K fromKey, boolean inclusive) */ SortedMap<K,V> tailMap(K fromKey); }
看了一下接口的方法,感觉用处很相似,返回的都是key值排序后其中的key或entry,这边也没太多说的,知道每个方法干什么的就行,下面开始正式的看TreeMap的源码,首先我们来了解一下很重要的一个内部类
static final class Entry<K,V> implements Map.Entry<K,V> { //key值 K key; //value值 V value; //左子节点 Entry<K,V> left = null; //右子节点 Entry<K,V> right = null; //父节点 Entry<K,V> parent; //节点颜色,黑色 boolean color = BLACK; /** * 创建新的entry,并赋予初始值 */ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } /** * 返回entry的key值 */ public K getKey() { return key; } /** * 返回value值 */ public V getValue() { return value; } /** * 设置value值 */ public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } /** * 判断节点是否相等 */ public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); } //计算哈希值的方法 public int hashCode() { int keyHash = (key==null ? 0 : key.hashCode()); int valueHash = (value==null ? 0 : value.hashCode()); return keyHash ^ valueHash; } //toString打印方法 public String toString() { return key + "=" + value; } }
上面的entry很重要,毕竟是由一个个节点构成的,所以我们要了解它的组成,下面看一下TreeMap的全局变量
/** * 比较器,用来对key值进行比较排序,如为null,则采用默认的 */ private final Comparator<? super K> comparator; /** * 用来存放entry的 */ private transient Entry<K,V> root = null; /** * entry的数量 */ private transient int size = 0; /** * 修改次数 */ private transient int modCount = 0;
TreeMap的全局变量就这些,也都比较简单,comparator一个用来排序的,TreeMap之所以有序就是这个的作用了,其他的都比较简单,下面我们继续看构造函数
/** * TreeMap无参构造,使用默认的比较 */ public TreeMap() { comparator = null; } /** * 参数为comparator,用给出的比较器进行排序 */ public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } /** * 将整个map放入TreeMap中 */ public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } /** * 创建一个新的TreeMap,包含传入参数map,排序方法相同 */ public TreeMap(SortedMap<K, ? extends V> m) { comparator = m.comparator(); try { buildFromSorted(m.size(), m.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } } /** * 将整个map放入TreeMap中 */ public void putAll(Map<? extends K, ? extends V> map) { //map大小 int mapSize = map.size(); //判断TreeMap无entry,传入参数元素不为0并且传入后map是SortedMap子类 if (size==0 && mapSize!=0 && map instanceof SortedMap) { //获得其比较器 Comparator c = ((SortedMap)map).comparator(); //条件比较器地址相同或者值相同 if (c == comparator || (c != null && c.equals(comparator))) { ++modCount; try { buildFromSorted(mapSize, map.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } return; } } //调用父类putAll方法 super.putAll(map); }
上面是TreeMap的几个构造方法,开篇说了由于红黑树相对比较复杂,所以这里我就挑一些常用的方法进行学习理解,如果有感兴趣的同学可以翻开源码深钻一下,应该可以收获很多,下面我们来看看一些常用的方法。
/** * 返回键值对个数 */ public int size() { return size; } /** * 根据key获取value */ public V get(Object key) { Entry<K,V> p = getEntry(key); return (p==null ? null : p.value); } /** * 根据key获取value具体实现 */ final Entry<K,V> getEntry(Object key) { // 比较器存在时,调用getEntryUsingComparator方法 if (comparator != null) return getEntryUsingComparator(key); //key为空,抛出异常 if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; //获取根节点 Entry<K,V> p = root; //遍历节点 while (p != null) { //把遍历出的节点key与key进行比较 int cmp = k.compareTo(p.key); //key小于当前节点key时,p左子节点移动到p位置 if (cmp < 0) p = p.left; //key大于当前节点key时,p右子节点移动到p位置 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) { //获取根节点 Entry<K,V> p = root; //根节点不为空时进入 while (p != null) { //比较两个key返回结果 int cmp = cpr.compare(k, p.key); //结果小于0,p移动到左子节点 if (cmp < 0) p = p.left; //结果大于0,p移动到右子节点 else if (cmp > 0) p = p.right; else return p; } } return null; }
上面介绍了get的时候,TreeMap内部是如何去操作的,接下来看看put
/** * key,value的形式向TreeMap中放值 */ public V put(K key, V value) { //根节点 Entry<K,V> t = root; //根节点为空时进入 if (t == null) { //比较key值,这里注释为类型检测 compare(key, key); // type (and possibly null) check //新建节点,并修改size root = new Entry<>(key, value, null); size = 1; modCount++; return null; } //比较后的返回值 int cmp; //父节点 Entry<K,V> parent; //比较器存在与不存在时分别做处理 Comparator<? super K> cpr = comparator; if (cpr != null) { //比较器存在时,遍历根节点 do { //更改父节点 parent = t; //将需要put的key与遍历出节点的key做比较 cmp = cpr.compare(key, t.key); //当参数key大于节点key时,t的左子节点代替t节点 if (cmp < 0) t = t.left; //当参数key小于节点key时,t的右子节点代替t节点 else if (cmp > 0) t = t.right; else //赋值并返回旧的value值 return t.setValue(value); } while (t != null); } else { //参数key为空抛出异常 if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; //遍历根节点,与之前相同操作 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); } //创建新的entry Entry<K,V> e = new Entry<>(key, value, parent); //如新增节点key小于parent的key时,作为parent的左子节点插入,否则作为右子节点插入 if (cmp < 0) parent.left = e; else parent.right = e; //对整体的树形结构进行调整,保持平衡 fixAfterInsertion(e); size++; modCount++; return null; } /** 调整整个树的整体结构 */ private void fixAfterInsertion(Entry<K,V> x) { //新增节点颜色为红色 x.color = RED; //循环,条件为x不为空,x不是父节点,x父节点颜色为红色,满足后跳出循环 while (x != null && x != root && x.parent.color == RED) { /** * parentOf(x)父节点 * leftOf(parentOf(parentOf(x)))父节点的父节点的左子节点 * 相等时进入 */ if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //获取父节点的父节点的右子节点 Entry<K,V> y = rightOf(parentOf(parentOf(x))); //如果当y节点为红色 if (colorOf(y) == RED) { //设置父节点颜色为黑色 setColor(parentOf(x), BLACK); //设置y节点为黑色 setColor(y, BLACK); //设置父节点的父节点的颜色为红色 setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { //当x为其父节点的右子节点 if (x == rightOf(parentOf(x))) { //将x的父节点作为x x = parentOf(x); //左旋 rotateLeft(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); //右旋 rotateRight(parentOf(parentOf(x))); } } else { //下面和上面差不多 Entry<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; } /** 右旋方法 */ private void rotateLeft(Entry<K,V> p) { if (p != null) { //获取p的右子节点 Entry<K,V> r = p.right; //将r的左子节点设置为p的右子节点 p.right = r.left; //r的左子节点不为空,则将p设置为r左子节点的父节点 if (r.left != null) r.left.parent = p; //将p的父节点设置为r的父节点 r.parent = p.parent; //p的父节点不为空时,将r设置为根节点,否则将r设为p的父节点的子节点 if (p.parent == null) root = r; else if (p.parent.left == p) p.parent.left = r; else p.parent.right = r; r.left = p; p.parent = r; } } /** 右旋方法 */ private void rotateRight(Entry<K,V> p) { if (p != null) { Entry<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; } }
在这里将右旋方法贴出,与左旋类似就不详细说明了,相信大家应该都能看懂。到这里基本上最常用的方法基本已经介绍完了,对于算法不好的LZ表示头已经快绕晕了,下面列几个简单点也能用到的方法结束TreeMap的解析
/** * 判断是否包含此key */ public boolean containsKey(Object key) { return getEntry(key) != null; } /** * 判断是否包含此value */ public boolean containsValue(Object value) { for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) if (valEquals(value, e.value)) return true; return false; } /** * 根据key值删除entry */ public V remove(Object key) { //获取entry Entry<K,V> p = getEntry(key); if (p == null) return null; V oldValue = p.value; //删除entry deleteEntry(p); return oldValue; } /** * 清空TreeMap */ public void clear() { modCount++; size = 0; root = null; }
好了,没有太深入的了解TreeMap,但是主要分析了几个常用方法的实现原理,如果有感兴趣想深入的同学,可以自己看看源码,相信对算法有兴趣的朋友应该还是会喜欢的
相关文章推荐
- TreeMap源码解析 给jdk写注释系列之jdk1.6容器(7)
- JDK源码之TreeMap源码解析
- TreeMap源码分析(基于JDK1.6)
- 【jdk源码解析一】java.util.Observer与java.util.Observable
- 通过JDK源码解析Thread(Runable target ...)调用的是哪个run方法
- JDk Set及HashSet源码解析
- jdk的动态代理源码解析
- TreeMap源码分析——基础分析(基于JDK1.6)
- JDK 源码解析 —— AtomicInteger
- 基于jdk动态代理的实现与源码解析
- JDK源码学习系列10----TreeMap
- Java TreeMap 源码解析
- java动态代理Proxy源码解析(Jdk 1.6)
- java ArrayList 源码解析(jdk1.6)
- BigInteger类实例的构造过程——JDK源码解析
- JDK源码-TreeMap
- TreeMap源码分析——基础分析(基于JDK1.6)
- JDK源码解析之ArrayList
- HashMap、HashTable、TreeMap 深入分析及源码解析
- JDK 源码解析 —— Java 内存模型