TreeMap分析
来源:互联网 发布:专业定制软件 编辑:程序博客网 时间:2024/06/05 03:33
TreeMap是基于红黑树实现的,红黑树是一种特殊的二叉树,百科一下介绍红黑树的性质:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
每次插入或者删除时,需要对二叉树进行调整,使得新树也满足红黑树限制。
构造函数 & 描述
1 TreeMap()
这个构造函数构造一个新的,空的树映射,使用键的自然顺序。
2 TreeMap(Comparator<? super K> comparator)
这个构造函数构造一个新的,空的树映射,根据给定的比较器进行排序。
3 TreeMap(Map<? extends K,? extends V> m)
这个构造函数构造一个新的树映射具有相同映射关系为给定的映射,根据其键的自然顺序进行排序。
4 TreeMap(SortedMap<K,? extends V> m)
SortedMap中的元素师有序的,这个构造函数构造一个新的树映射具有相同映射关系,并使用相同的顺序在指定的有序映射。
存储结构
TreeMap的排序是基于对key的排序实现的,它的每一个Node代表红黑树的一个节点,Node的数据结构如下:
static class Node<K, V> implements Map.Entry<K, V> { //父节点 Node<K, V> parent; //左节点 Node<K, V> left; //右节点 Node<K, V> right; //键 final K key; //值 V value; //高度 int height; Node(Node<K, V> parent, K key) { this.parent = parent; this.key = key; this.height = 1; } Node<K, V> copy(Node<K, V> parent) { Node<K, V> result = new Node<K, V>(parent, key); if (left != null) { result.left = left.copy(result); } if (right != null) { result.right = right.copy(result); } result.value = value; result.height = height; return result; }
插入删除:
使用一组状态,把查找,创建,等操作进行封装,当root==null时,并且状态为CREATE,则创建一个新Node
public V put(K key, V value) { return putInternal(key, value); }
V putInternal(K key, V value) { //查找该节点,并且创建一个新节点 Node<K, V> created = find(key, Relation.CREATE); V result = created.value; created.value = value; return result; }
Node<K, V> find(K key, Relation relation) { if (root == null) { if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) { throw new ClassCastException(key.getClass().getName() + " is not Comparable"); // NullPointerException ok } //创建一个新节点 if (relation == Relation.CREATE) { root = new Node<K, V>(null, key); size = 1; modCount++; return root; } else { return null; } } @SuppressWarnings("unchecked") Comparable<Object> comparableKey = (comparator == NATURAL_ORDER) ? (Comparable<Object>) key : null; Node<K, V> nearest = root; while (true) { //传入比较器则用传入的比较,否则用系统的compareTo比较,得到一个comparison值,用来觉得node在树中的位置 int comparison = (comparableKey != null) ? comparableKey.compareTo(nearest.key) : comparator.compare(key, nearest.key); if (comparison == 0) { switch (relation) { case LOWER: //lower返回小于等于key,且最接近key得节点 //比如 1,2,3,5 key=4,没有找到4,返回最接近的一个值且小于等于4,所以返回key=3的值 return nearest.prev(); case FLOOR: case EQUAL: case CREATE: case CEILING: //返回key=该值得node return nearest; case HIGHER: //higher返回大于等于key,且最接近key得节点 return nearest.next(); } } //comparsion<0则为左节点,comparsion>0则为右节点 Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right; if (child != null) { nearest = child; continue; } /* * We found a nearest node. Every key not in the tree has up to two * nearest nodes, one lower and one higher. */ if (comparison < 0) { // nearest.key is higher switch (relation) { case LOWER: case FLOOR: return nearest.prev(); case CEILING: case HIGHER: return nearest; case EQUAL: return null; case CREATE: //创建一个新节点,指向nearest的左节点 Node<K, V> created = new Node<K, V>(nearest, key); nearest.left = created; size++; modCount++; //调整二叉树保持红黑树特性 rebalance(nearest, true); return created; } } else { // comparison > 0, nearest.key is lower switch (relation) { case LOWER: case FLOOR: return nearest; case CEILING: case HIGHER: return nearest.next(); case EQUAL: return null; case CREATE: //创建一个新节点,指向nearest的右节点 Node<K, V> created = new Node<K, V>(nearest, key); nearest.right = created; size++; modCount++; //调整二叉树保持红黑树特性 rebalance(nearest, true); return created; } } } }
插入操作都是同个find方法完成,通过上面分析,基本上清楚了是怎么插入元素的,剩下rebalance方法这里不分析,是红黑树旋转问题,主要是调整二叉树保持平衡。
public void remove() { if (last == null) { throw new IllegalStateException(); } //删除一个节点 removeInternal(last); expectedModCount = modCount; last = null; }
void removeInternal(Node<K, V> node) { //先记录下要删除节点左右节点,及父节点 Node<K, V> left = node.left; Node<K, V> right = node.right; Node<K, V> originalParent = node.parent; if (left != null && right != null) { //有左右子节点情况 //找出相邻节点,比较左右子树高度,选择高度较高子树 // 1 1 // / \ / \ // *2* 3 ---》 4 3 // / \ / / \ / // 4 5 6 7 5 6 // / //7 // 假设选中2节点删除,2的左子树高度比较高,那么2相邻的子节点,adjacent 就为4,反之则为5 Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first(); removeInternal(adjacent); // takes care of rebalance and size-- int leftHeight = 0; left = node.left; if (left != null) { //断开node节点与左子节点连接 leftHeight = left.height; adjacent.left = left; left.parent = adjacent; node.left = null; } int rightHeight = 0; right = node.right; if (right != null) { //断开node节点与右子节点连接 rightHeight = right.height; adjacent.right = right; right.parent = adjacent; node.right = null; } adjacent.height = Math.max(leftHeight, rightHeight) + 1; //断开node节点与父节点连接,并且让相邻节点adjacent,连接上node节点的父节点 replaceInParent(node, adjacent); return; } else if (left != null) { //该节点只有左子节点,把子节点置空 replaceInParent(node, left); node.left = null; } else if (right != null) { //该节点只有右子节点,把子节点置空 replaceInParent(node, right); node.right = null; } else { replaceInParent(node, null); } rebalance(originalParent, false); size--; modCount++; }
删除方法比较绕,阅读的时候最好是画图理解,remove中剩下replaceInParent方法,接下来就看下它做了什么:
private void replaceInParent(Node<K, V> node, Node<K, V> replacement) { //先把node父节点保存到临时变量,然后断开与父节点的连接 Node<K, V> parent = node.parent; node.parent = null; //如果相邻节点不为null,那么相邻节点父节点指向原node节点的父节点 if (replacement != null) { replacement.parent = parent; } //node父节点不为null,那么node不是root节点 if (parent != null) { //node节点是左节点,那么相邻节点就替换node父节点的左子节点 if (parent.left == node) { parent.left = replacement; } else { //相邻节点就替换node父节点的右子节点 // assert (parent.right == node); parent.right = replacement; } } else { //node为root节点,故相邻节点直接替换root root = replacement; } }
删除操作分析完了,其他操作就不再分析,这里总结几点:
1、TreeMap的查询、插入、删除效率均没有HashMap高,每次插入删除,如果破坏了红黑树平衡,需要对树旋转操作,一般只有要对key排序时才使用TreeMap。2、TreeMap的key不能为null,而HashMap的key可以为null。3、TreeMap是根据key进行排序的,排序和定位需要依赖比较器或覆写Comparable接口,因此不需要key覆写hashCode方法和equals方法,就可以排除掉重复的key,而HashMap的key则需要通过覆写hashCode方法和equals方法来确保没有重复的key。
0 0
- TreeMap分析
- TreeMap分析
- TreeMap分析
- TreeMap排序 & HashMap分析
- TreeMap源码分析
- TreeMap源码分析二
- TreeMap源码分析三
- TreeMap源码分析五
- TreeMap源码分析六
- TreeMap源码分析七
- TreeMap源码分析八
- TreeMap源码分析九
- TreeMap源码分析十
- 《Java源码分析》:TreeMap
- 源码分析-TreeMap
- 【Java集合】TreeMap分析
- TreeMap源码分析
- TreeMap源码分析
- java微信公共帐号支付(JS支付)
- maven+jetty开发环境配置随笔
- 快速了解C/C++的左值和右值
- 基于java聊聊架构
- 文章标题
- TreeMap分析
- 题目1167:数组排序 北航
- ISMS与信息安全的三观论
- 最近在C++_MVVM模式开发中一些小问题
- 1038. Recover the Smallest Number (30)
- 《Javascript高级程序设计》读书笔记之——Prototype
- 反思过去,以期少走弯路
- docker的php容器安装 memcached
- Extjs与Struts2交互,返回JSON数据的两种实现方式