Java集合系列---TreeMap源码解析(巨好懂!!!)

5 篇文章 0 订阅
4 篇文章 0 订阅

TreeMap底层是基于红黑树实现,能实现根据key值对节点进行排序,排序可分为自然排序和定制排序。
自然排序:TreeMap的所有key必须实现Comparable接口,
定制排序:创建TreeMap的时候,传入一个Comparator对象,该对象负责对TreeMap的所有key进行排序,不需要key实现Comparable接口。

1 基本属性和构造器
//基本属性
	//比较器
	private final Comparator<? super K> comparator;
	//表示红黑树的根节点
    private transient Entry<K,V> root;
    //TreeMap中的元素个数
    private transient int size = 0;
    //TreeMap修改次数
    private transient int modCount = 0;
	static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;//对于key值
    V value;//对于value值
    Entry<K,V> left;//指向左子树的引用
    Entry<K,V> right;//指向右子树的引用
    Entry<K,V> parent;//指向父节点的引用
    boolean color = BLACK;//节点的颜色默认是黑色
	}

//构造方法,comparator用键的顺序做比较
public TreeMap() {
    comparator = null;
}

//构造方法,提供比较器,用指定比较器排序
public TreeMap(Comparator<? super K> comparator) {
    his.comparator = comparator;
}

//将m中的元素转化daoTreeMap中,按照键的顺序做比较排序
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

//构造方法,指定的参数为SortedMap
//采用m的比较器排序
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) {
    }
}  
2 核心方法
1) put
public V put(K key, V value) {
        Entry<K,V> t = root; //记录根节点
        if (t == null) {
            compare(key, key); // type (and possibly null) check 检查是否为空
			//构造根节点,因为根节点没有父节点,传入null值。
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; //获取比较器
        if (cpr != null) { //如果获取了比较器则采用比较器进行比较
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)//如果key < t.key , 指向左子
                    t = t.left;
                else if (cmp > 0) //如果key > t.key , 指向它的右孩子节点
                    t = t.right;
                else
                    return t.setValue(value);//如果它们相等,替换key的值
            } while (t != null);
        }
        else {//自然排序
            if (key == null) //抛出异常
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                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<K,V> e = new Entry<>(key, value, parent);//创建新节点,并制定父节点 此时父节点已经遍历到合适的位置
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);//新插入节点后重新调整红黑树 
        size++;
        modCount++;
        return null;
    }
2)比较方法
//比较方法,如果comparator==null ,采用comparable.compartTo进行比较,否则采用指定比较器比较大小
final int compare(Object k1, Object k2) {
    return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
        : comparator.compare((K)k1, (K)k2);
}
3)红黑树的调整方法
 private void fixAfterInsertion(Entry<K,V> x) {
 		//插入节点默认是红色的
        x.color = RED;
		//情形1: 新节点x 是树的根节点,没有父节点不需要任何操作
    	//情形2: 新节点x 的父节点颜色是黑色的,也不需要任何操作
        while (x != null && x != root && x.parent.color == RED) {
        	/*情形3:新节点x的父节点颜色是红色的
   			 判断x的节点的父节点位置,是否属于左孩子*/
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            	//获取x节点的父节点的兄弟节点,上面语句已经判断出x节点的父节点为左孩子,所以直接取右孩子
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {//判断是否x节点的父节点的兄弟节点为红色。
                    setColor(parentOf(x), BLACK);// x节点的父节点设置为黑色
                    setColor(y, BLACK);// 父亲节点的兄弟节点颜色设置为黑色
                    setColor(parentOf(parentOf(x)), RED); // x.parent.parent设置为红色
                    x = parentOf(parentOf(x));
                } else {
                //x的父节点的兄弟节点是黑色或者缺少的
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);//左旋转操作
                    }
                     //现在x节点是其父的左孩子
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));//进行右旋转
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));//y 是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 是其父亲的左孩子
                        x = parentOf(x);
                        rotateRight(x);//以父节点为旋转点,进行右旋操作
                    }
                    setColor(parentOf(x), BLACK);//父节点为设置为黑色
                    setColor(parentOf(parentOf(x)), RED);//祖父节点为设置为红色
                    rotateLeft(parentOf(parentOf(x)));//以父节点为旋转点,进行左旋操作
                }
            }
        }
        root.color = BLACK;//通过节点位置的调整,最终将红色的节点条调换到了根节点的位置,根节点重新设置为黑色
    }

在网上找到的一份讲解
在这里插入图片描述

4)get方法

其实就是获取红黑树的根节点然后遍历获取到该节点

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) // 使用比较器的getEntry版本,返回与指定键对应的项
            return getEntryUsingComparator(key);
        if (key == null)	//  如果指定键为 null 并且此映射使用自然顺序
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key; // 使用自然顺序比较器
        Entry<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;
    }
5)遍历方法

主要还是调用keySet方法,返回的keySet实现了Iterable接口,可以返回一个迭代器,该迭代器的具体实现是KeyIterator,而 KeyIterator 类的核心逻辑是在PrivateEntryIterator中实现的。然后通过nextEntry方法来循环获取下一个值。

public Set<K> keySet() {
        return navigableKeySet();
    }
public NavigableSet<K> navigableKeySet() {
    KeySet<K> nks = navigableKeySet;
    return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
}

static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
    private final NavigableMap<E, ?> m;
    KeySet(NavigableMap<E,?> map) { m = map; }

    public Iterator<E> iterator() {
        if (m instanceof TreeMap)
            return ((TreeMap<E,?>)m).keyIterator();
        else
            return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
    }

    // 省略非关键代码
}

Iterator<K> keyIterator() {
    return new KeyIterator(getFirstEntry());
}

final class KeyIterator extends PrivateEntryIterator<K> {
    KeyIterator(Entry<K,V> first) {
        super(first);
    }
    public K next() {
        return nextEntry().key;
    }
}

abstract class PrivateEntryIterator<T> implements Iterator<T> {
    Entry<K,V> next;
    Entry<K,V> lastReturned;
    int expectedModCount;

    PrivateEntryIterator(Entry<K,V> first) {
        expectedModCount = modCount;
        lastReturned = null;
        next = first;
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Entry<K,V> nextEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // 寻找节点 e 的后继节点
        next = successor(e);
        lastReturned = e;
        return e;
    }

    // 其他方法省略
}
6) remove
public V remove(Object key) {
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    deleteEntry(p);
    return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
        modCount++; //记录修改的次数
        size--;//数量减一

        //判断它是否有两个孩子
        if (p.left != null && p.right != null) {
            Entry<K,V> s = successor(p);//寻找继承者,继承者为当前节点的右孩子节点或者右孩子节点的最小左孩子
            p.key = s.key;//替换key和value
            p.value = s.value;
            p = s;//指向继承者
        } // p has 2 children

  		//如果p找到替代节点的话,就是对替代节点的操作,否则的话就是对p的操作
    	//根据上面的属性可以知道不可能存在左右两个孩子都存在的情况,successor寻找的就是最小节点
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);
        if (replacement != null) {//判断最小节点是否有右孩子
            // Link replacement to parent
            replacement.parent = p.parent;
            if (p.parent == null)//如果p没有父节点的话
                root = replacement;//则指向根节点
                //如果p为左孩子,则将p.parent.left = p.left
            else if (p == p.parent.left)//判断p是不是父节点的左节点
                p.parent.left  = replacement;
            else
                p.parent.right = replacement;
            // Null out links so they are OK to use by fixAfterDeletion.
            //清空p节点
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)//调整红黑树
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { // return if we are the only node.
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            if (p.color == BLACK)
                fixAfterDeletion(p);

            if (p.parent != null) {
                if (p == p.parent.left)
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null;
            }
        }
    }
  • 注意到当p是有左右节点的时候,对红黑树的调整是fixAfterDeletion(replacement);,传入的参数不是p,这是因为该节点的颜色并没有修改,所以需要考虑顶替被它替换掉的节点位置的平衡,故传入的是replacement,其他情况需要调整的都是p
7) fixAfterDeletion
private void fixAfterDeletion(Entry<K,V> x) {
    //不是根节点,颜色为黑色,调整结构
    while (x != root && colorOf(x) == BLACK) {

        //判断x是否为左孩子
        if (x == leftOf(parentOf(x))) {
            //x的兄弟节点
            Entry<K,V> sib = rightOf(parentOf(x));
            //若兄弟节点是红色
            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);   //设置兄弟节点变为黑色
                setColor(parentOf(x), RED);  //父节点设置为红色
                rotateLeft(parentOf(x));   //左旋父节点
                sib = rightOf(parentOf(x)); //重新设置x的兄弟节点
            }

            if (colorOf(leftOf(sib))  == BLACK &&
                colorOf(rightOf(sib)) == BLACK) {
                setColor(sib, RED); //兄弟节点的两个孩子都是黑色的重新设置兄弟节点的颜色,修改为红色
                x = parentOf(x);   //将x定位到父节点
            } else {
                if (colorOf(rightOf(sib)) == BLACK) {   //兄弟节点的右孩子是黑色的,左孩子是红色的
                    setColor(leftOf(sib), BLACK);  //设置左孩子节点为黑色
                    setColor(sib, RED); //兄弟节点为红色
                    rotateRight(sib);   //右旋
                    sib = rightOf(parentOf(x));  //右旋后重新设置兄弟节点
                }
                setColor(sib, colorOf(parentOf(x)));  //兄弟节点颜色设置和父节点的颜色相同
                setColor(parentOf(x), BLACK);   //父节点设置为黑色
                setColor(rightOf(sib), BLACK);  //将兄弟节点的有孩子设置为黑色
                rotateLeft(parentOf(x));   //左旋
                x = root;  //设置x为根节点
            }
        } else { // symmetric
            //x为父节点的右节点,参考上面的操作
            Entry<K,V> sib = leftOf(parentOf(x));

            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateRight(parentOf(x));
                sib = leftOf(parentOf(x));
            }

            if (colorOf(rightOf(sib)) == BLACK &&colorOf(leftOf(sib)) == BLACK) {
                    setColor(sib, RED);
                    x = parentOf(x);
            } else {
                if (colorOf(leftOf(sib)) == BLACK) {
                    setColor(rightOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateLeft(sib);
                    sib = leftOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(leftOf(sib), BLACK);
                rotateRight(parentOf(x));
                x = root;
            }
        }
    }

    setColor(x, BLACK);
}

在这里插入图片描述
参考博客:
https://segmentfault.com/a/1190000012775806

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值