TreeMap

作者: sizuoyi00 | 来源:发表于2019-10-16 00:32 被阅读0次

1.TreeMap结构

基于红黑树(Red-Black tree)的 NavigableMap 实现。
特点:迭代有序,TreeMap的key和value值不允许为Null,TreeMap的key必须实现Comparable(自然排序)或者指定Comparator(定制化排序)
关于循环删除,增强for循环删除会修改modCount++,导致fail-fast,所以会报错ConcurrentModificationException
迭代器循环删除节点后并没有对modCount修改,所以迭代器支持删除

2.TreeMap内部结构

/** 成员变量信息 */
// 构造器取值,该值决定排序使用自然顺序还是Comparator定制化排序
private final Comparator<? super K> comparator;
// 红黑树根节点
private transient Entry<K,V> root;
// 长度
private transient int size = 0;
// fail-fast机制
private transient int modCount = 0;
// TreeMap的红黑树节点对应的集合,entryset迭代
private transient EntrySet entrySet;
// TreeMap键的KeySet集合,keyset迭代
private transient KeySet<K> navigableKeySet;
// TreeMap键值对的倒序 待研究
private transient NavigableMap<K,V> descendingMap;
// 节点颜色
private static final boolean RED   = false;
private static final boolean BLACK = true;

/** 内部类信息 */
// 内部类 实现红黑树
static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }
    }
}

2.TreeMap构造器

    // 默认构造,使用Comparable自然排序
    public TreeMap() {
        comparator = null;
    }
    // 指定comparator,定制排序(如按某个类的某字段排序)
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    // 同默认构造,使用Comparable自然排序
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(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) {
        }
    }

3.查找(get方法)

遍历红黑树,从根节点开始,遍历方式同红黑树,key小于根节点key则往左节点查,key大于根节点key则往右节点查

  public V get(Object key) {
        TreeMap.Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
    }

    final TreeMap.Entry<K,V> getEntry(Object key) {
        // Offload comparator-based version for sake of performance
        if (comparator != null)
            return getEntryUsingComparator(key);
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
        TreeMap.Entry<K,V> p = root;
        //红黑树不为空则遍历
        //二叉查找树遍历逻辑 key小于根节点key则往左节点查,key大于根节点key则往右节点查,
        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;
    }

4.新增(put方法)

遍历红黑树,寻找插入节点 -> 绑定新节点与父节点关系 -> 修复红黑树

  public V put(K key, V value) {
      TreeMap.Entry<K,V> t = root;
      // 根节点为 null,将新节点设为根节点
      if (t == null) {
          compare(key, key); // type (and possibly null) check

          root = new TreeMap.Entry<>(key, value, null);
          size = 1;
          modCount++;
          return null;
      }
      int cmp;
      TreeMap.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)
                  t = t.left;
              else if (cmp > 0)
                  t = t.right;
              else
                  return t.setValue(value);
          } while (t != null);
      }
      else {
          if (key == null)
              throw new NullPointerException();
          @SuppressWarnings("unchecked")
          Comparable<? super K> k = (Comparable<? super K>) key;
          // 遍历红黑树 寻找插入节点
          do {
              //parent赋值为最小父节点
              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);
      }
      // 新节点信息 parent最小父节点,绑定新节点与父节点信息
      TreeMap.Entry<K,V> e = new TreeMap.Entry<>(key, value, parent);
      // 绑定父节点的子节点信息=新节点
      if (cmp < 0)
          parent.left = e;
      else
          parent.right = e;
      //修复红黑树性质,调节颜色,结构,根节点等
      fixAfterInsertion(e);
      size++;
      modCount++;
      return null;
  }

5.删除(remove方法)

根据删除节点的子节点数目判断删除方法,通过后继节点替换,完成隔p链接,部分情况需要修复节点

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--;

    // p有两个子节点
    if (p.left != null && p.right != null) {
        // 找到后继节点
        Entry<K,V> s = successor(p);
        // 将p赋值并指向其后继节点
        p.key = s.key;
        p.value = s.value;
        // 将p引用为后继节点
        p = s;
    } // p has 2 children

    // Start fixup at replacement node, if it exists.
    // 注意当p有两个子节点时,p为后继节点, p的子节点为新的替换节点
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);

    // 删除节点存在一个子节点(左或右),用父节点直接链接删除节点的子节点(替换节点)
    if (replacement != null) {
        // 将替换节点(子节点或后继节点)的父节点与p的父节点连线,隔p连线
        replacement.parent = p.parent;
        if (p.parent == null)
            root = replacement;
        else if (p == p.parent.left)
            // 父节点的左子树改为替换节点
            p.parent.left  = replacement;
        else
            // 父节点的右子树改为替换节点
            p.parent.right = replacement;

        // Null out links so they are OK to use by fixAfterDeletion.
        p.left = p.right = p.parent = null;

        // 删除黑色需要调整红黑树
        if (p.color == BLACK)
            fixAfterDeletion(replacement);
    } else if (p.parent == null) { 
        // 删除的是根节点,且树中有且仅有一个节点
        root = null;
    } else { 
        // 删除的节点没有孩子节点
        
        // p是黑色,则需要进行调整
        if (p.color == BLACK)
            fixAfterDeletion(p);

        // 将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;
        }
    }
}

5.1查前驱后继

    /**
     * 后继节点:大于当前节点值并且值最小的节点
     */
    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 {
            // 这里如果不理解,简单画张结点图就可以很清晰
            // 没有右子树的情况,首先分析下,其左子树都是比当前结点小的,所以不用考虑左子树有没有的情况
            // 再思考下,1、如果当前结点为父节点的左子结点,那么最小值就是父节点
            // 2、如果当前结点为父节点的右子结点,往父节点思考,从父节点一直往上找,
            // 直到找到某结点为其父节点的左孩子时,这个父节点就是后继节点
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            // 父节点不为空,且当前结点为右子结点,则父节点为后继
            while (p != null && ch == p.right) {
                ch = p;
                // 注意 这里返回的是parent
                p = p.parent;
            }
            return p;
        }
    }

    /**
     * 前继节点:小于当前节点值并且值最大的节点
     */
    static <K,V> Entry<K,V> predecessor(Entry<K,V> t) {
        if (t == null)
            return null;
        else if (t.left != null) {
            Entry<K,V> p = t.left;
            while (p.right != null)
                p = p.right;
            return p;
        } else {
            // 这里如果不理解,简单画张结点图就可以很清晰
            // 没有左子树的情况,首先分析下,其右子树都是比当前结点大的,所以不用考虑右子树有没有的情况
            // 再思考下,1、如果当前结点为父节点的右子结点,那么最大值就是父节点
            // 2、如果当前结点为父节点的左子结点,往父节点思考,从父节点一直往上找,
            // 直到找到某结点为其父节点的右孩子时,这个父节点就是前继节点
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            // 父节点不为空,且当前结点为左子结点,则父节点为前驱节点
            while (p != null && ch == p.left) {
                ch = p;
                // 注意 这里返回的是parent
                p = p.parent;
            }
            return p;
        }
    }

6.遍历

关于循环删除,增强for循环删除会修改modCount++,导致fail-fast,所以会报错ConcurrentModificationException
迭代器循环删除节点后并没有对modCount修改,所以迭代器支持删除

    // treeMap.entrySet() 遍历迭代器
    final class EntryIterator extends TreeMap.PrivateEntryIterator<Map.Entry<K,V>> {
        EntryIterator(TreeMap.Entry<K,V> first) {
            super(first);
        }
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }

    // treeMap.values() 遍历迭代器
    final class ValueIterator extends TreeMap.PrivateEntryIterator<V> {
        ValueIterator(TreeMap.Entry<K,V> first) {
            super(first);
        }
        public V next() {
            return nextEntry().value;
        }
    }

    // treeMap.keySet() 遍历迭代器
    final class KeyIterator extends TreeMap.PrivateEntryIterator<K> {
        KeyIterator(TreeMap.Entry<K,V> first) {
            super(first);
        }
        public K next() {
            // 正序
            return nextEntry().key;
        }
    }

    // treeMap.descendingKeySet() 倒序遍历迭代器
    final class DescendingKeyIterator extends TreeMap.PrivateEntryIterator<K> {
        DescendingKeyIterator(TreeMap.Entry<K,V> first) {
            super(first);
        }
        public K next() {
            // 倒序
            return prevEntry().key;
        }
        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            deleteEntry(lastReturned);
            lastReturned = null;
            expectedModCount = modCount;
        }
    }

    abstract class PrivateEntryIterator<T> implements Iterator<T> {
        TreeMap.Entry<K,V> next;
        // 当前遍历的最后一个节点
        TreeMap.Entry<K,V> lastReturned;
        int expectedModCount;

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

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

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

        // DescendingKeyIterator 倒序遍历迭代器调用
        final TreeMap.Entry<K,V> prevEntry() {
            TreeMap.Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            // fail-fast
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // 寻找e的前继节点
            next = predecessor(e);
            lastReturned = e;
            return e;
        }

        // 这里删除节点后并没有对modCount修改,所以迭代器支持删除
        // 而增强for循环删除会modCount++,与构造器生成的modCount不等,则会删除异常
        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            // fail-fast
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // deleted entries are replaced by their successors
            if (lastReturned.left != null && lastReturned.right != null)
                // 删除当前结点,将当前结点值赋值给下一节点,防止遍历出问题
                next = lastReturned;
            // 删除节点,更给结点关系
            deleteEntry(lastReturned);
            // 重新赋值expectedModCount
            expectedModCount = modCount;
            lastReturned = null;
        }
    }

相关文章

  • TreeMap了解一下

    前言 TreeMap TreeMap类继承图 TreeMap的域 TreeMap的构造函数 TreeMap常见Ap...

  • TreeMap

    还是从几个常用的方法如数: TreeMap.TreeMap() : TreeMap.put() :   目前不清楚...

  • TreeMap及Set源码解析

    1、本文主要内容 TreeMap及Set介绍 TreeMap源码解析 Set源码解析 2、TreeMap及Set介...

  • lambda HashMap 排序

    TreeMap 按key排序生成map可以有TreeMap 完成,TreeMap可以按key的自然顺序排序(Com...

  • Java集合TreeMap用法总结

    Java的TreeMap是集合框架中的一个实现类,TreeMap继承了AbstractMap。TreeMap实现了...

  • java8中treemap源码分析

    分析大纲: treemap中的实现原理 treemap中的remove()(红黑树的删除实践) treemap中的...

  • Java集合类-TreeMap

    TreeMap和HashMap的区别和共同点 TreeMap 简介 TreeMap 是一个有序的key-value...

  • TreeMap简介

    TreeMap是支持排序的map,基于红黑树,无容量限制,TreeMap非线程安全。 TreeMap继承Abstr...

  • python pyecharts绘制矩形树图Treemap

    @[toc] treemap_base treemap_levels echarts_option_query

  • JDK源码解析——TreeMap

    第1部分 TreeMap介绍 TreeMap 简介 TreeMap 是一个有序的key-value集合,它是通过红...

网友评论

      本文标题:TreeMap

      本文链接:https://www.haomeiwen.com/subject/ixjvyctx.html