TreeMap put()和遍历

这篇博客记录了学习TreeMap时关于put()方法和遍历TreeMap的解析,方便巩固学习,仅作参考。

 

TreeMap treeMap = new TreeMap<>() ;
String str1 = "12";
String str2 = "123";
String str3 = "1234";
treeMap.put(str1, "1");
treeMap.put(str2, "2");
treeMap.put(str3, "3");
System.out.println("TreeMap not set iterator");
Iterator> iterator = treeMap.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry entry = (Map.Entry) iterator.next();
	System.out.println("[" + entry.getKey() + " ," + entry.getValue() + "]");
}

控制台打印结果: 

TreeMap not set iterator
[12 ,1]
[123 ,2]
[1234 ,3]

在解析源码之前,粗略解释一下Comparable接口实现:

实现了Comparable接口的类的实例之间是可以相互比较的。在实现Comparable时,会将实例的一个特征或多个特征作为重要判定依据,按照某种方法得出的值(这里假设为compareVal),实例间做相互比较。如果实例A的compareVal 大于 实例B的compareVal, A.compareTo(B)返回 1, B.compareTo(A)返回 -1,则对实例的排序是升序的。如果实例A的compareVal 大于 实例B的compareVal, A.compareTo(B)返回 -1, B.compareTo(A)返回 1,则对实例的排序是降序的。

举一个简单的例子:

 Person类

public class Person implements Comparable{
    int age;
    String name;
    ...
    // 按照age来比较(升序)
    public int compareTo(Person person) {
        
        return this.age - person.age;
    }
    
    /**
     * // 按照age来比较 (降序)
     * public int compareTo(Person person) {
     *     return person.age - this.age;   
     * }
     */
}

TreeMap put(K key, V value)源码解析

public V put(K key, V value) {
        Entry t = root;
        // 第一次向容器中放入元素(k/v)时,创建根节点(root)
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry parent;

        // 默认的比较器(comparator)是空的,在创建TreeMap容器时传入一个比较器。
        // 所以按照上面代码创建TreeMap容器,比较器是空的。
        // split comparator and comparable paths
        Comparator 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();
            // 如果没有设定比较器,就使用Key对应类的比较方式。所以,下面这行代码就是得到
            // key所实现的比较方式。key先跟父节点(最开始时父节点就是根节点root)的key比较,小于则 
            // 继续跟父节点的左子节点比较,大于则跟父节点的右子节点,以此类推,直到找到与e的key相
            // 等的节点或者父节点的左子节点或者右子节点为null。      
            // 当找到与e的key相等的节点时,替换该节点的value,当父节点的    
            // 左子节点(右子节点)为null时,就创建一个节点作为父节点的左子节点(右子节点)

            @SuppressWarnings("unchecked")
                Comparable k = (Comparable) 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 e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

TreeMap遍历,源码解析:

Iterator> iterator = treeMap.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry entry = (Map.Entry) iterator.next(); 		
}

treeMap.entrySet().iterator()内部源码调用顺序: 

->  TreeMap.entrySet()  返回一个EntrySet的集合

->  new EntrySet() 

->  EntrySet.iterator()  返回一个基于EntrySet集合的迭代器EntryIterator

->   new EntryIterator() 

->  TreeMap.getFirstEntry() 得到整个树的最左边的节点

iterator.next()内部源码调用顺序: 

->  EntryIterator.next()

->  PrivateEntryIterator.nextEntry()

->  successor(e)  整个迭代器最主要的迭代代码在successor(e)中

// 得到Entry的set集合
public Set> entrySet() {
    EntrySet es = entrySet;
    return (es != null) ? es : (entrySet = new EntrySet());
}
// TreeMap的内部类EntrySet 
class EntrySet extends AbstractSet> {
        public Iterator> iterator() {
            return new EntryIterator(getFirstEntry());                                        
        }
}
// TreeMap内部类 EntryIterator (迭代器)
// EntryIterator 继承PrivateEntryIterator类
final class EntryIterator extends PrivateEntryIterator> {
        EntryIterator(Entry first) {
            super(first);
        }
        public Map.Entry next() {
            return nextEntry();
        }
 }

 // PrivateEntryIterator
 abstract class PrivateEntryIterator implements Iterator {
        Entry next;
        Entry lastReturned;
        int expectedModCount;

        PrivateEntryIterator(Entry first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first; // 初始化EntryIterator时,next指向整个树的最左边的叶子节点
        }
 
  public final boolean hasNext() {
      return next != null; // 当没有节点时,迭代终止。
  }
  
  final Entry nextEntry() {
      Entry e = next; // 第一次调用这个方法时,e指向整个树的最左边的叶子节点
      if (e == null)
          throw new NoSuchElementException();
      if (modCount != expectedModCount)
          throw new ConcurrentModificationException();
      // 整个迭代器最主要的迭代代码在successor(e)中,successor方法返回下一个节点next,下一次遍    
      // 历时,next又用作参数e,作为基础节点来寻找下一个返回节点。
      next = successor(e); 
      lastReturned = e;
      return e; // 执行successor方法后返回e。
   }

}


final Entry getFirstEntry() {
        Entry p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
 }

 TreeMap的getFirstEntry()方法,找到整个树的最左边的叶子节点,参考下图:即节点 D

                   A
          B                   C
    D          E        F        G
   


static  TreeMap.Entry successor(Entry t) {
        // 第一次调用时 t 指向整个树的最左边的叶子节点 D(如上图)
        if (t == null)
            return null;
        // 如果节点t有右节点t_right,但是t_right 没有左节点,就返回t_right(即t的右节点)
        // 如果节点t有右节点t_right,并且t_right有左节点t_right_left,就以t_right为父节点,
        // 返回其下最左边的子节点。
        else if (t.right != null) { 
            Entry p = t.right; 
            while (p.left != null)
                p = p.left;
            return p;
        } else {
            // 节点t没有右节点
            // 并且t不是一个右子节点,就返回t的父节点
            // 如果节点t是一个右节点,就返回t的父节点的父节点
            Entry p = t.parent; 
            Entry ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p; //注意这里返回的节点P用作下一次迭代的基础,而不是iterator.next()的返回值
                      // iterator.next()返回值是节点 t
        }
    }

以上图所示的树结构图为例,以iterator.next()的调用顺序为例:

调用前next指的是调用前next指向的节点

调用后next指的是调用后next指向的节点

次数 调用前next 调用后next 返回节点 分析
1 D B D D节点没有右节点,但是D节点不是一个右节点,所以调用后next()方法后,next指向D的父节点B。
2 B E B B节点有右节点E,但是E节点没有左节点,所以调用后next()方法后,next指向B的右节点E。
3 E A E E没有右节点,并且B节点是一个右节点,所以调用后next()方法后,next指向E的父节点B的父节点A。

依次类推,整个树的遍历结果是DBEAFCG。

根据TreeMap的遍历代码可以看出遍历的顺序是左节点 -> 父节点 -> 右节点,是中序遍历。

自定义TreeMap的比较器

通过自定义比较器来控制整个树的节点顺序。这里为了偷懒,采用与String排序相反的方式。

TreeMap treeMapWithIterator = new TreeMap<>((x, y)->{
			return y.compareTo(x);
});
treeMapWithIterator.put(str1, "1");
treeMapWithIterator.put(str2, "2");
treeMapWithIterator.put(str3, "3");
System.out.println("降序:");
// TreeMap是用中序遍历
Iterator> iterator2 = treeMapWithIterator.entrySet().iterator();
while (iterator2.hasNext()) {
		Map.Entry entry = (Map.Entry) iterator2.next();
		System.out.println("[" + entry.getKey() + " ," + entry.getValue() + "]");
}

下面是没有自定义比较器的测试,默认使用String实例的比较方式:

TreeMap treeMap = new TreeMap<>() ;
String str1 = "12";
String str2 = "123";
String str3 = "1234";
treeMap.put(str1, "1");
treeMap.put(str2, "2");
treeMap.put(str3, "3");
System.out.println("升序:");
Iterator> iterator = treeMap.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry entry = (Map.Entry) iterator.next();
	System.out.println("[" + entry.getKey() + " ," + entry.getValue() + "]");
}

看看两者的输出结果对比:

升序
[12 ,1]
[123 ,2]
[1234 ,3]
降序:
[1234 ,3]
[123 ,2]
[12 ,1]

 在构建TreeMap时如果没设定比较器,那么往TreeMap中放入元素时就会使用Key对应的类实现的比较方式。
 如果既没有设定比较器,Key对应的类也未实现Comparable接口,那么就会出现ClassCastException
 参考put()方法的这行代码:    Comparable k = (Comparable) key

仅作参考,有疏漏或者不正确的地方欢迎指出,相互学习。

你可能感兴趣的:(Java核心,TreeMap,put,遍历,中序,java)