• TreeMap实现原理及源码分析之JDK8


    转载 Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例

    一、TreeMap 简单介绍

    什么是Map?

      在数组中我们通过数组下标来对数组内容进行索引的,而在Map中我们通过对象来对 对象进行索引,用来索引的对象叫做key,其对应的对象叫做value。这就是我们平时说的键值对。

    什么是TreeMap?

      TreeMap是一个有序的key-value集合,是非线程安全的,基于红黑树(Red-Black tree)实现。其映射根据键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。其基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。

      当自定义比较器时,需要自定义类实现java.lang.Comparable接口,并重写compareTo()方法。

    TreeMap与HashMap的区别:

    • 数据结构不同:
      • HashMap是基于哈希表,由 数组+链表+红黑树 构成。
      • TreeMap是基于红黑树实现。
    • 存储方式不同:
      • HashMap是通过key的hashcode对其内容进行快速查找。
      • TreeMap中所有的元素都保持着某种固定的顺序。
    • 排列顺序:
      • HashMap存储顺序不固定。
      • TreeMap存储顺序固定,可以得到一个有序的结果集。

    二、TreeMap源码分析

     1.TreeMap类继承图:

    TreeMap类定义:

    public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable
    • TreeMap<K,V>:TreeMap是以key-value形式存储数据的。
    • extends AbstractMap<K,V>:继承了AbstractMap,大大减少了实现Map接口时需要的工作量。
    • implements NavigableMap<K,V>:实现了SortedMap,支持一系列的导航方法。比如返回有序的key集合。
    • implements Cloneable:表明其可以调用克隆方法clone()来返回实例的field-for-field拷贝。
    • implements Serializable:表明该类是可以序列化的。

    2.类成员变量和静态内部类Entry:

    // 比较器对象
    private final Comparator<? super K> comparator;
    
    // 根节点
    private transient Entry<K,V> root;
    
    // 集合大小
    private transient int size = 0;
    
    // 树结构被修改的次数
    private transient int modCount = 0;

     // 红黑树节点颜色
     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; 
    }

    关键字transient的作用:

      transient是Java语言的关键字,它被用来表示一个域中不是该对象串行化的一部分。
      Java的 serialization 提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
    当一个对象被串行化的时候,transient型的变量值 不包括在串行化的表示中,而 非transient型的变量 是被包括进去的。

    3.TreeMap的构造函数:

    // 默认构造函数。使用默认比较器比较key的大小,TreeMap中的元素按照自然排序进行排列。
    public TreeMap() {
       // 默认比较机制 comparator
    = null; } // 带比较器的构造函数 public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } // 带Map的构造函数,Map会成为TreeMap的子集 public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } // 带SortedMap的构造函数,SortedMap会成为TreeMap的子集 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) { } }

    putAll(Map<? extends K, ? extends V> map) 方法:

    /**
     * Map中的所有元素添加到TreeMap中
     */
    public void putAll(Map<? extends K, ? extends V> map) {
        int mapSize = map.size();
        // TreeMap的size为0,map的size不为0,并且map是SortedMap的实例
        if (size==0 && mapSize!=0 && map instanceof SortedMap) {
            Comparator<?> c = ((SortedMap<?,?>)map).comparator();
            // 默认比较器等于map的比较器,或者map的比较器不为null,并且与默认比较器相等
            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);
    }
    
    /**
     * Map中的所有元素添加到TreeMap中
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        // 遍历map一个一个添加到TreeMap中
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    buildFromSorted(int size, Iterator<?> it, java.io.ObjectInputStream str, V defaultVal) 方法:

    /**
     * SortedMap(有序的map)中的所有元素添加到TreeMap中
     */
    private void buildFromSorted(int size, Iterator<?> it,
                                 java.io.ObjectInputStream str,
                                 V defaultVal)
        throws  java.io.IOException, ClassNotFoundException {
        this.size = size;
        root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
                               it, str, defaultVal);
    }
    
    /**
     * 将map中的元素逐个添加到TreeMap中,并返回map的中间元素作为根节点
     */
    private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
                                             int redLevel,
                                             Iterator<?> it,
                                             java.io.ObjectInputStream str,
                                             V defaultVal)
        throws  java.io.IOException, ClassNotFoundException {
    
        if (hi < lo) return null;
    
        // 获取中间元素
        int mid = (lo + hi) >>> 1;
    
        Entry<K,V> left  = null;
        
        // 若lo小于mid,则递归调用获取(middel的)左孩子。
        if (lo < mid)
            left = buildFromSorted(level+1, lo, mid - 1, redLevel,
                                   it, str, defaultVal);
    
        // 从Iterator或stream中获取middle节点对应的key和value
        K key;
        V value;
        if (it != null) {
            if (defaultVal==null) {
                Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
                key = (K)entry.getKey();
                value = (V)entry.getValue();
            } else {
                key = (K)it.next();
                value = defaultVal;
            }
        } else { // use stream
            key = (K) str.readObject();
            value = (defaultVal != null ? defaultVal : (V) str.readObject());
        }
    
        // 创建middle节点
        Entry<K,V> middle =  new Entry<>(key, value, null);
    
        // 若当前节点的深度==红色节点的深度,则将节点着色为红色
        if (level == redLevel)
            middle.color = RED;
    
        // 设置middle为left的父亲,left为middle的左孩子
        if (left != null) {
            middle.left = left;
            left.parent = middle;
        }
    
        if (mid < hi) {
            // 递归调用获取(middel的)右孩子
            Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
                                               it, str, defaultVal);
            // 设置middle为right的父亲,right为middle的右孩子
            middle.right = right;
            right.parent = middle;
        }
    
        return middle;
    }

      添加到红黑树中时,只将level == redLevel的节点设为红色。表示第level级节点,实际上是用buildFromSorted方法转换成红黑树后 的最底端的节点(假设根节点在最上方);只将红黑树最底端的级别 着色为红色,其余都是黑色。

    4.核心方法:

    红黑树相关的方法:

    rotateLeft(Entry<K,V> p) 方法:

    /**
     * 左旋
     */
    private void rotateLeft(Entry<K,V> p) {
        if (p != null) {
            
            Entry<K,V> r = p.right; // 令p节点右孩子为r节点
            p.right = r.left; // 令r节点的左孩子为 p节点的右孩子
            if (r.left != null)
                r.left.parent = p; // 当r节点的左孩子不为null时,令p节点为 r节点的左孩子 的父节点
            r.parent = p.parent; // 令p节点的父节点为 r节点的父节点
            if (p.parent == null)
                root = r; // 当p节点的父节点为null时,令r节点为根节点
            else if (p.parent.left == p)
                p.parent.left = r; // 当p节点的父节点的左孩子为p节点时,令r节点为 p节点的父节点的左孩子
            else
                p.parent.right = r; // 当p节点的父节点的右孩子为p节点时,令r节点为 p节点的父节点的右孩子
            r.left = p; // 令p节点为 r节点的左孩子
            p.parent = r; // 令r节点为 p节点的父节点
        }
    }

    put(K key, V value) 方法:

    /**
     * 插入操作
     */
    public V put(K key, V value) {
        Entry<K,V> t = root; // 获取根节点
        if (t == null) {
            compare(key, key); // 检查key的类型,是否为null
    
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        
        // 获取比较器
        Comparator<? super K> cpr = comparator;
        // 比较器不为null时,即自定义了比较器
        if (cpr != null) {
            // 循环比较插入节点的key与根节点的key的大小,确定插入节点的位置,即找到插入节点的父节点
            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); // 插入节点与根节点的key的大小相同,直接覆盖
            } while (t != null);
        }
        // 比较器为null时,使用默认的比较器
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            // 循环比较插入节点的key与根节点的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;
        // 插入修正操作,使插入节点后,TreeMap还是红黑树结构
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

    与红黑树有关的方法还有:

    • 右旋-rotateRight(Entry<K,V> p)
    • 插入修正为红黑树-fixAfterInsertion(Entry<K,V> x)
    • 删除-deleteEntry(Entry<K,V> p)
    • 删除修正为红黑树-fixAfterDeletion(Entry<K,V> x)

    都是根据算法翻译成代码,具体可参考这里。

    TreeMap中Entry相关的方法:

      TreeMap的 firstEntry()、 lastEntry()、 lowerEntry()、 higherEntry()、 floorEntry()、 ceilingEntry()、 pollFirstEntry() 、 pollLastEntry() 原理类似,以下讲解firstEntry()方法。

    firstEntry() 方法:

    /**
     * 获取第一个节点
     */
    public Map.Entry<K,V> firstEntry() {
        return exportEntry(getFirstEntry());
    }
    
    /**
     * 获取第一个节点
     */
    final Entry<K,V> getFirstEntry() {
        // 获取根节点
        Entry<K,V> p = root; 
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
    }
    
    /**
     * 获取第一个节点
     */
    static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
        // 如果节点为null,创建AbstractMap.SimpleImmutableEntry类型的对象,并返回
        return (e == null) ? null :
            new AbstractMap.SimpleImmutableEntry<>(e);
    }
    public static class SimpleImmutableEntry<K,V>
        implements Entry<K,V>, java.io.Serializable
    {
        private static final long serialVersionUID = 7138329143949025153L;
    
        private final K key;
        private final V value;
    
        public SimpleImmutableEntry(K key, V value) {
            this.key   = key;
            this.value = value;
        }
    
        public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
            this.key   = entry.getKey();
            this.value = entry.getValue();
        }
    
        public K getKey() {
            return key;
        }
    
        public V getValue() {
            return value;
        }
    
        public V setValue(V value) {
            throw new UnsupportedOperationException();
        }
    
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            return eq(key, e.getKey()) && eq(value, e.getValue());
        }
    
        public int hashCode() {
            return (key   == null ? 0 :   key.hashCode()) ^
                   (value == null ? 0 : value.hashCode());
        }
    
        public String toString() {
            return key + "=" + value;
        }
    
    }
    SimpleImmutableEntry 类的实现

      从上面,我们可以看出 firstEntry() 和 getFirstEntry() 都是用于获取第一个节点。但是,firstEntry() 是对外接口; getFirstEntry() 是内部接口。而且,firstEntry() 是通过 getFirstEntry() 来实现的。那为什么外界不能直接调用 getFirstEntry(),而需要多此一举的调用 firstEntry() 呢?

      这么做的目的是:防止用户修改返回的Entry。getFirstEntry()返回的Entry是可以被修改的,但是经过firstEntry()返回的Entry不能被修改,只可以读取Entry的key值和value值。因为exportEntry()方法所在类 SimpleImmutableEntry 的 setValue()方法会抛出 UnsupportedOperationException() 异常。

    TreeMap中key相关方法:

      TreeMap的firstKey()、lastKey()、lowerKey()、higherKey()、floorKey()、ceilingKey()原理都是类似的,下面以ceilingKey()来进行详细说明。

    ceilingKey(K key) 方法:

    /**
     * 获取大于/等于key的最小节点所对应的key,没有的话返回null
     */
    public K ceilingKey(K key) {
        return keyOrNull(getCeilingEntry(key));
    }
    /**
     * 寻找大于/等于key的最小节点
     */
    final Entry<K,V> getCeilingEntry(K key) {
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = compare(key, p.key);
            // 如果根节点的key大于给定key
            if (cmp < 0) {
                if (p.left != null)
                    p = p.left;
                else
                    return p;
            } else if (cmp > 0) { // 如果根节点的key小于给定key
                if (p.right != null) {
                    p = p.right;
                } else { // 如果根节点的右孩子为null
                    Entry<K,V> parent = p.parent;
                    Entry<K,V> ch = p;
                    while (parent != null && ch == parent.right) {
                        ch = parent;
                        parent = parent.parent;
                    }
                    return parent;
                }
            } else
                return p;
        }
        return null;
    }
    /**
     * 如果节点不为null,返回节点的key值,否则返回null
     */
    static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) {
        return (e == null) ? null : e.key;
    }

    TreeMap中value()方法:

    value() 方法返回 TreeMap中值的集合:

    /**
     * 通过 new Values() 来实现,返回TreeMap中值的集合
     * Values() 是集合类Value的构造函数
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values();
            values = vs;
        }
        return vs;
    }
    
    /**
     * 集合类Value
     */
    class Values extends AbstractCollection<V> {
        // 返回迭代器
        public Iterator<V> iterator() {
            // iterator() 通过ValueIterator() 返回迭代器
            return new ValueIterator(getFirstEntry());
        }
        
        // 返回个数
        public int size() {
            return TreeMap.this.size();
        }
    
        // TreeMap的值的集合中 是否包含 对象o
        public boolean contains(Object o) {
            return TreeMap.this.containsValue(o);
        }
    
        // 删除TreeMap的值的集合中的对象o
        public boolean remove(Object o) {
            for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) {
                if (valEquals(e.getValue(), o)) {
                    deleteEntry(e);
                    return true;
                }
            }
            return false;
        }
    
        // 清空TreeMap的值的集合
        public void clear() {
            TreeMap.this.clear();
        }
    
        public Spliterator<V> spliterator() {
            return new ValueSpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
        }
    }
    
    /**
     * ValueIterator类实现 Iterator接口实现的next()方法
     */
    final class ValueIterator extends PrivateEntryIterator<V> {
            ValueIterator(Entry<K,V> first) {
            super(first);
        }
        public V next() {
            return nextEntry().value;
        }
    }
    /**
     * PrivateEntryIterator类实现 Iterator接口的hasNext()和remove()方法
     * ValueIterator类实现 Iterator接口实现的next()方法
     */
    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();
            next = successor(e);
            lastReturned = e;
            return e;
        }
    
        // 返回上一节点
        final Entry<K,V> prevEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = predecessor(e);
            lastReturned = e;
            return e;
        }
    
        // 删除当前节点
        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            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 = modCount;
            lastReturned = null;
        }
    }
    PrivateEntryIterator 类

    TreeMap的entrySet()方法:

     entrySet() 方法返回 TreeMap的键值对的集合:

    /**
     * 通过 new EntrySet() 来实现,返回TreeMap的键值对集合
     */
    public Set<Map.Entry<K,V>> entrySet() {
        EntrySet es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }
    
    /**
     * EntrySet是TreeMap的所有键值对组成的集合,它的单位是单个键值对
     */
    class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        // 返回迭代器
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator(getFirstEntry());
        }
    
        // EntrySet中是否包含 键值对Object
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
            Object value = entry.getValue();
            Entry<K,V> p = getEntry(entry.getKey());
            return p != null && valEquals(p.getValue(), value);
        }
    
        // 删除EntrySet中的 键值对Object
        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
            Object value = entry.getValue();
            Entry<K,V> p = getEntry(entry.getKey());
            if (p != null && valEquals(p.getValue(), value)) {
                deleteEntry(p);
                return true;
            }
            return false;
        }
    
        // 返回EntrySet中元素个数
        public int size() {
            return TreeMap.this.size();
        }
    
        // 清空EntrySet
        public void clear() {
            TreeMap.this.clear();
        }
    
        public Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
        }
    }
    
    /**
     * EntryIterator类实现 Iterator接口实现的next()方法
     */
    final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
        EntryIterator(Entry<K,V> first) {
            super(first);
        }
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }

    TreeMap实现的Cloneable接口:

    TreeMap实现了Cloneable接口,即实现了clone()方法。
    clone()方法的作用很简单,就是克隆一个TreeMap对象并返回。

    /**
     * 克隆一个TreeMap,并返回Object对象
     */
    public Object clone() {
        TreeMap<K,V> clone = null;
        try {
            clone = (TreeMap<K,V>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    
        // Put clone into "virgin" state (except for comparator)
        clone.root = null;
        clone.size = 0;
        clone.modCount = 0;
        clone.entrySet = null;
        clone.navigableKeySet = null;
        clone.descendingMap = null;
    
        // Initialize clone with our mappings
        try {
            clone.buildFromSorted(size, entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    
        return clone;
    }

    TreeMap实现的Serializable接口:

    TreeMap实现java.io.Serializable,分别实现了串行读取和写入功能:

    • 串行写入函数是writeObject(),它的作用是将TreeMap的“容量和所有的Entry”都写入到输出流中。
    • 串行读取函数是readObject(),它的作用是将TreeMap的“容量和所有的Entry”依次读出。

    readObject() 和 writeObject() 正好是一对,通过它们,我能实现TreeMap的串行传输。

    /**
     * java.io.Serializable的写入函数
     * 将TreeMap的 容量和所有的Entry 都写入到输出流中
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out the Comparator and any hidden stuff
        s.defaultWriteObject();
    
        // Write out size (number of Mappings)
        s.writeInt(size);
    
        // Write out keys and values (alternating)
        for (Iterator<Map.Entry<K,V>> i = entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<K,V> e = i.next();
            s.writeObject(e.getKey());
            s.writeObject(e.getValue());
        }
    }
    
    /**
     * java.io.Serializable的读取函数:根据写入方式读出
     * 将TreeMap的 容量和所有的Entry 依次读出
     */
    private void readObject(final java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in the Comparator and any hidden stuff
        s.defaultReadObject();
    
        // Read in size
        int size = s.readInt();
    
        buildFromSorted(size, null, s, null);
    }

    三、TreeMap使用例子

    1.TreeMap常用方法使用Demo:

    import java.util.*;
    
    /**
     * @author nana
     * @date 2019/2/23
     */
    public class TreeMapDemo {
    
        public static void main(String[] args) {
            // 测试常用的API
            testTreeMapOrdinaryAPIs();
    
            // 测试TreeMap导航函数
            testNavigableMapAPIs();
    
            // 测试TreeMap的子Map函数
            testSubMapAPIs();
        }
    
        /**
         * 测试常用的API
         */
        private static void testTreeMapOrdinaryAPIs() {
            // 生成随机数
            Random random = new Random();
    
            // 创建TreeMap实例
            TreeMap treeMap = new TreeMap();
            treeMap.put("one", random.nextInt(10));
            treeMap.put("two", random.nextInt(10));
            treeMap.put("three", random.nextInt(10));
    
            System.out.println("TreeMapDemo.testTreeMapOrdinaryAPIs-Begin");
    
            // 打印TreeMap
            System.out.printf("打印treeMap:
    %s
    ", treeMap);;
    
            // 通过Iterator遍历key-value
            Iterator iterator = treeMap.entrySet().iterator();
            System.out.println("通过Iterator遍历key-value:");
            while (iterator.hasNext()) {
                Map.Entry entity = (Map.Entry) iterator.next();
                System.out.printf("%s-%s
    ", entity.getKey(), entity.getValue());
            }
    
            // TreeMap的键值对个数
            System.out.printf("TreeMap的键值对个数:%s
    ", treeMap.size());
    
            // 是否包含key
            System.out.println("是否包含key:");
            System.out.printf("是否包含key:one-%s
    ", treeMap.containsKey("one"));
            System.out.printf("是否包含key:four-%s
    ", treeMap.containsKey("four"));
    
            // 删除key对应的键值对
            System.out.println("删除key对应的键值对:");
            treeMap.remove("one");
            System.out.printf("删除key为one的键值对后,treeMap为:
    %s
    ", treeMap);
    
            // 清空TreeMap的节点
            System.out.println("清空treeMap的节点:");
            treeMap.clear();
            System.out.printf("%s
    ", treeMap.isEmpty() ? "treeMap is empty!" : "treeMap is not empty!");
            System.out.printf("%s
    ", treeMap == null ? "treeMap is null!" : "treeMap is not null!");
    
            System.out.println("TreeMapDemo.testTreeMapOrdinaryAPIs-End");
        }
    
        /**
         * 测试TreeMap导航函数
         */
        private static void testNavigableMapAPIs() {
            // 创建TreeMap实例,TreeMap是 NavigableMap接口的实现类
            NavigableMap navigableMap = new TreeMap();
            navigableMap.put("aaa",1);
            navigableMap.put("bbb",2);
            navigableMap.put("ccc",3);
            navigableMap.put("ddd",4);
    
            System.out.println("TreeMapDemo.testNavigableMapAPIs-Begin");
    
            // 打印TreeMap
            System.out.printf("打印navigableMap:
    %s
    ", navigableMap);
    
            // 获取第一个key和节点
            System.out.printf("First key:%s	First entry:%s
    ", navigableMap.firstKey(), navigableMap.firstEntry());
    
            // 获取最后一个key和节点
            System.out.printf("Last key:%s	Last entry:%s
    ", navigableMap.lastKey(), navigableMap.lastEntry());
    
            // 获取小于/等于 key为bbb 最大的key和节点
            System.out.printf("Key floor before bbb:%s	%s
    ", navigableMap.floorKey("bbb"), navigableMap.floorEntry("bbb"));
    
            // 获取小于 key为bbb 最大的key和节点
            System.out.printf("Key lower before bbb:%s	%s
    ", navigableMap.lowerKey("bbb"), navigableMap.lowerEntry("bbb"));
    
            // 获取大于/等于 key为bbb 最大的key和节点
            System.out.printf("Key ceiling after bbb:%s	%s
    ", navigableMap.ceilingKey("bbb"), navigableMap.ceilingEntry("bbb"));
    
            // 获取大于 key为bbb 最大的key和节点
            System.out.printf("Key higher after bbb:%s	%s
    ", navigableMap.higherKey("bbb"), navigableMap.higherEntry("bbb"));
    
            System.out.println("TreeMapDemo.testNavigableMapAPIs-End");
        }
    
        /**
         * 测试TreeMap的子Map函数
         */
        private static void testSubMapAPIs() {
            // 实例化TreeMap对象
            TreeMap treeMap = new TreeMap();
            treeMap.put("a",1);
            treeMap.put("b",2);
            treeMap.put("c",3);
            treeMap.put("d",4);
    
            System.out.println("TreeMapDemo.testSubMapAPIs-Begin");
    
            // 打印TreeMap
            System.out.printf("打印TreeMap:
    %s
    ", treeMap);
    
            // 打印 key为c节点 前的节点(默认不包含c节点)
            System.out.printf("打印 key为c节点 前的节点(默认不包含c节点):%s", treeMap.headMap("c"));
    
            System.out.printf("打印 key为c节点 前的节点(包含c节点):%s
    ", treeMap.headMap("c", true));
            System.out.printf("打印 key为c节点 前的节点(不包含c节点):%s
    ", treeMap.headMap("c", false));
    
            // 打印 key为c节点 后的节点(默认包含c节点)
            System.out.printf("打印 key为c节点 后的节点(默认包含c节点):%s
    ", treeMap.tailMap("c"));
    
            System.out.printf("打印 key为c节点 后的节点(包含c节点)%s
    ", treeMap.tailMap("c", true));
            System.out.printf("打印 key为c节点 后的节点(不包含c节点)%s
    ", treeMap.tailMap("c", false));
    
            // 打印 key为a与c节点 之间的节点(默认不包含c节点)
            System.out.printf("打印 key为a与c节点 之间的节点(默认包含c节点):
    %s
    ", treeMap.subMap("a", "c"));
    
            System.out.printf("打印 key为a与c节点 之间的节点(包含a、c节点):
    %s
    ", treeMap.subMap("a", true, "c", true));
            System.out.printf("打印 key为a与c节点 之间的节点(包含a节点):
    %s
    ", treeMap.subMap("a", true, "c", false));
            System.out.printf("打印 key为a与c节点 之间的节点(包含c节点):
    %s
    ", treeMap.subMap("a", false, "c", true));
            System.out.printf("打印 key为a与c节点 之间的节点(不包含a、c节点):
    %s
    ", treeMap.subMap("a", false, "c", false));
    
            // 正序打印TreeMap的key
            System.out.printf("正序打印TreeMap的key:
    %s
    ", treeMap.navigableKeySet());
            // 倒序打印TreeMap的key
            System.out.printf("倒序打印TreeMap的key:
    %s
    ", treeMap.descendingKeySet());
    
            System.out.println("TreeMapDemo.testSubMapAPIs-End");
        }
    }
    TreeMapDemo
    TreeMapDemo.testTreeMapOrdinaryAPIs-Begin
    打印treeMap:
    {one=2, three=5, two=1}
    通过Iterator遍历key-value:
    one-2
    three-5
    two-1
    TreeMap的键值对个数:3
    是否包含key:
    是否包含key:one-true
    是否包含key:four-false
    删除key对应的键值对:
    删除key为one的键值对后,treeMap为:
    {three=5, two=1}
    清空treeMap的节点:
    treeMap is empty!
    treeMap is not null!
    TreeMapDemo.testTreeMapOrdinaryAPIs-End
    TreeMapDemo.testNavigableMapAPIs-Begin
    打印navigableMap:
    {aaa=1, bbb=2, ccc=3, ddd=4}
    First key:aaa    First entry:aaa=1
    Last key:ddd    Last entry:ddd=4
    Key floor before bbb:bbb    bbb=2
    Key lower before bbb:aaa    aaa=1
    Key ceiling after bbb:bbb    bbb=2
    Key higher after bbb:ccc    ccc=3
    TreeMapDemo.testNavigableMapAPIs-End
    TreeMapDemo.testSubMapAPIs-Begin
    打印TreeMap:
    {a=1, b=2, c=3, d=4}
    打印 key为c节点 前的节点(默认不包含c节点):{a=1, b=2}打印 key为c节点 前的节点(包含c节点):{a=1, b=2, c=3}
    打印 key为c节点 前的节点(不包含c节点):{a=1, b=2}
    打印 key为c节点 后的节点(默认包含c节点):{c=3, d=4}
    打印 key为c节点 后的节点(包含c节点){c=3, d=4}
    打印 key为c节点 后的节点(不包含c节点){d=4}
    打印 key为a与c节点 之间的节点(默认包含c节点):
    {a=1, b=2}
    打印 key为a与c节点 之间的节点(包含a、c节点):
    {a=1, b=2, c=3}
    打印 key为a与c节点 之间的节点(包含a节点):
    {a=1, b=2}
    打印 key为a与c节点 之间的节点(包含c节点):
    {b=2, c=3}
    打印 key为a与c节点 之间的节点(不包含a、c节点):
    {b=2}
    正序打印TreeMap的key:
    [a, b, c, d]
    倒序打印TreeMap的key:
    [d, c, b, a]
    TreeMapDemo.testSubMapAPIs-End
    TreeMapDemo 运行结果

    2.TreeMap遍历使用Demo:

    import org.springframework.util.StringUtils;
    
    import java.util.*;
    
    /**
     * @author nana
     * @date 2019/2/23
     */
    public class TreeMapIteratorTest {
    
        public static void main(String[] args) {
            // 创建treeMap对象
            TreeMap treeMap = treeMapTest();
    
            // 通过entrySet()遍历TreeMap的节点
            iteratorTreeMapByEntrySet(treeMap);
            // 通过keySet()遍历TreeMap的节点
            iteratorTreeMapByKeySet(treeMap);
            // 遍历TreeMap的value
            iteratorTreeMapByValue(treeMap);
        }
    
        /**
         * 创建treeMap对象
         * @return
         */
        private static TreeMap treeMapTest() {
            String key = null;
            int keyValue = 0;
            Integer value = null;
            Random random = new Random();
            TreeMap treeMap = new TreeMap();
            int i = 0;
            while (i < 6) {
                // 随机获取[0,50)的整数
                keyValue = random.nextInt(50);
                key = String.valueOf(keyValue);
                value = random.nextInt(10);
                // 添加到treeMap中
                treeMap.put(key, value);
                i++;
            }
            return treeMap;
        }
    
        /**
         * 通过entrySet()遍历TreeMap的节点
         */
        private static void iteratorTreeMapByEntrySet(TreeMap treeMapTest) {
            if (StringUtils.isEmpty(treeMapTest)) {
                return;
            }
            // 遍历TreeMap
            Iterator iterator = treeMapTest.entrySet().iterator();
            System.out.println("通过entrySet()遍历TreeMap的节点:");
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                System.out.printf("%s-%s	", entry.getKey(), entry.getValue());
            }
        }
    
        /**
         * 通过keySet()遍历TreeMap的节点
         */
        private static void iteratorTreeMapByKeySet(TreeMap treeMapTest) {
            if(StringUtils.isEmpty(treeMapTest)) {
                return;
            }
            String key = null;
            Integer value = null;
            Iterator iterator = treeMapTest.keySet().iterator();
            System.out.println("
    通过keySet()遍历TreeMap的节点:");
            while (iterator.hasNext()) {
                key = (String) iterator.next();
                value = (Integer) treeMapTest.get(key);
                System.out.printf("%s-%s	", key, value);
            }
        }
    
        /**
         * 遍历TreeMap的value
         */
        private static void iteratorTreeMapByValue(TreeMap treeMapTest) {
            if (treeMapTest == null) {
                return;
            }
            Collection collection = treeMapTest.values();
            Iterator iterator = collection.iterator();
            System.out.println("
    遍历TreeMap的value:");
            while (iterator.hasNext()) {
                System.out.printf("%s	", iterator.next());
            }
        }
        
    }
    TreeMapIteratorTest
    通过entrySet()遍历TreeMap的节点:
    19-0    2-7    20-1    22-0    34-3    
    通过keySet()遍历TreeMap的节点:
    19-0    2-7    20-1    22-0    34-3    
    遍历TreeMap的value:
    0    7    1    0    3    
    TreeMapIteratorTest 运行结果
  • 相关阅读:
    检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)
    maven跳过单元测试-maven.test.skip和skipTests的区别
    java JFR
    Docker常用命令
    关键字group by 、 Having的 用法
    css特效
    sql
    初识Hibernate之理解持久化类
    Hibernate 搭建
    基本 SQL 之增删改查
  • 原文地址:https://www.cnblogs.com/nananana/p/10426377.html
Copyright © 2020-2023  润新知