TreeMap的底层实现

TreeMap的基本概念:

· TreeMap集合是基于红黑树(Red-Black tree)的 NavigableMap实现。该集合最重要的特点就是可排序,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。这句话是什么意思呢?就是说TreeMap可以对添加进来的元素进行排序,可以按照默认的排序方式,也可以自己指定排序方式。

· 根据上一条,我们要想使用TreeMap存储并排序我们自定义的类(如User类),那么必须自己定义比较机制:一种方式是User类去实现java.lang.Comparable接口,并实现其compareTo()方法。另一种方式是写一个类(如MyCompatator)去实现java.util.Comparator接口,并实现compare()方法,然后将MyCompatator类实例对象作为TreeMap的构造方法参数进行传参(当然也可以使用匿名内部类),这些比较方法是怎么被调用的将在源码中讲解。

· 下图是Map集合体系类图。

 

正文

TreeMap源码分析

1,类名及类成员变量

public class TreeMap<K,V>

    extends AbstractMap<K,V>

    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

{

    // 比较器对象

    private final Comparator<? super K> comparator;

 

    // 根节点

    private transient Entry<K,V> root;

 

    // 集合大小

    private transient int size = 0;

 

    // 树结构被修改的次数

    private transient int modCount = 0;

 

    // 静态内部类用来表示节点类型

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

    }

}

2,类构造方法

    public TreeMap() {   // 1,无参构造方法

        comparator = null; // 默认比较机制

    }

 

    public TreeMap(Comparator<? super K> comparator) { // 2,自定义比较器的构造方法

        this.comparator = comparator;

    }

 

    public TreeMap(Map<? extends K, ? extends V> m) {  // 3,构造已知Map对象为TreeMap

        comparator = null; // 默认比较机制

        putAll(m);

    }

    public TreeMap(SortedMap<K, ? extends V> m) { // 4,构造已知的SortedMap对象为TreeMap

        comparator = m.comparator(); // 使用已知对象的构造器

        try {

            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);

        } catch (java.io.IOException cannotHappen) {

        } catch (ClassNotFoundException cannotHappen) {

        }

    }

3put()方法详解

    public V put(K key, V value) {

        Entry<K,V> t = root;  // 获取根节点

 

        // 如果根节点为空,则该元素置为根节点

        if (t == null) {

            compare(key, key); // type (and possibly null) check

 

            root = new Entry<>(key, value, null);

            size = 1;    // 集合大小为1

            modCount++;  // 结构修改次数自增

            return null;

        }

 

        int cmp;

        Entry<K,V> parent;

        Comparator<? super K> cpr = comparator;  // 比较器对象

 

        // 如果比较器对象不为空,也就是自定义了比较器

        if (cpr != null) {   

            do { // 循环比较并确定元素应插入的位置(也就是找到该元素的父节点)

                parent = t;  // t就是root

 

                // 调用比较器对象的compare()方法,该方法返回一个整数

                cmp = cpr.compare(key, t.key);

                if (cmp < 0)      // 待插入元素的key"小于"当前位置元素的key,则查询左子树

                    t = t.left;

                else if (cmp > 0) // 待插入元素的key"大于"当前位置元素的key,则查询右子树

                    t = t.right;

                else              // "相等"则替换其value

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

                cmp = k.compareTo(t.key); // 同样调用比较方法并返回一个整数

                if (cmp < 0)       // 待插入元素的key"小于"当前位置元素的key,则查询左子树

                    t = t.left;

                else if (cmp > 0)  // 待插入元素的key"大于"当前位置元素的key,则查询右子树

                    t = t.right;

                else               // "相等"则替换其value

                    return t.setValue(value);

            } while (t != null);

        }

 

        Entry<K,V> e = new Entry<>(key, value, parent);  // 根据key找到父节点后新建一个节点

        if (cmp < 0)  // 根据比较的结果来确定放在左子树还是右子树

            parent.left = e;

        else

            parent.right = e;

        fixAfterInsertion(e);

        size++;      // 集合大小+1

        modCount++;  // 集合结构被修改次数+1

        return null;

    }

3.1,自定义比较器的使用。说了这么多关于比较器的内容,不上手试试这么能行?

· 先来看下面这段代码

package com.jimmy.map;

 

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import java.util.TreeMap;

 

public class TreeMapDemo2 {

    public static void main(String[] args) {

        Map<String, String> map = new TreeMap<>();

 

        map.put("ddd", "444");

        map.put("ccc", "333");

        map.put("bbb", "222");

        map.put("aaa", "111");

 

        Set<Entry<String, String>> entrySet = map.entrySet();

        for (Entry<String, String> each : entrySet) {

            System.out.println(each.getKey()+"::"+each.getValue());

        }

    }

}

输出结果如下,结果是排序过的,为什么呢?那是因为String类实现了Comparable接口并实现了compareTo()方法,该方法按字典顺序比较两个字符串,请自行查看其实现。

aaa::111

bbb::222

ccc::333

ddd::444

· 下面我们写个自定义User类,使用2种方式将类对象按照age字段从小到大排序。

方式1User实现Comparable接口并实现了compareTo()方法

User

package com.jimmy.domain;

 

public class User implements Comparable<User>{

    private String username;

    private int age;

 

    public User(String username, int age) {

        this.username = username;

        this.age = age;

    }

 

    @Override

    public String toString() {

        return "User [username=" + username + ", age=" + age + "]";

    }

    @Override

    public int compareTo(User user) {

        int temp = this.age - user.age;

        return temp == 0 ? this.username.compareTo(user.username) : temp;

    }   

}

测试代码

package com.jimmy.map;

 

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import java.util.TreeMap;

 

import com.jimmy.domain.User;

 

public class TreeMapDemo1 {

    public static void main(String[] args) {

        Map<User, String> map = new TreeMap<>();

 

        map.put(new User("jimmy1", 30), "hello");

        map.put(new User("jimmy2", 30), "hello");

        map.put(new User("jimmy", 22), "hello");

        map.put(new User("jimmy", 20), "hello");

 

        Set<Entry<User, String>> entrySet = map.entrySet();

        for (Entry<User, String> each : entrySet) {

            System.out.println(each.getKey()+"::"+each.getValue());

        }

    }

}

输出结果如下,首先按age排序,若年龄相等则再按username的字母表顺序排序。

User [username=jimmy, age=20]::hello

User [username=jimmy, age=22]::hello

User [username=jimmy1, age=30]::hello

User [username=jimmy2, age=30]::hello

方式2,写一个类实现java.util.Comparator接口,并将该类对象传递给TreeMap的构造方法。这种方式将实体类和比较机制解耦合,可以写很多个不同的比较器对象。

实体类

package com.jimmy.domain;

 

public class User3 {  // User对象不再实现任何接口

    private String username;

    private int age;

 

    public User3(String username, int age) {

        super();

        this.username = username;

        this.age = age;

    }

    public String getUsername() {

        return username;

    }

    public void setUsername(String username) {

        this.username = username;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "User3 [username=" + username + ", age=" + age + "]";

    }

 

}

比较器类

package com.jimmy.map;

 

import java.util.Comparator;

import com.jimmy.domain.User3;

 

public class TreeMapComparator implements Comparator<User3>{  // 比较器类

 

    @Override

    public int compare(User3 o1, User3 o2) {

        int temp = o1.getAge() - o2.getAge();

        return temp == 0 ? o1.getUsername().compareTo(o2.getUsername()) : temp;

    }

}

测试代码

package com.jimmy.map;

 

import java.util.Map;

import java.util.Set;

import java.util.TreeMap;

import java.util.Map.Entry;

 

import com.jimmy.domain.User3;

 

public class TreeMapDemo3 {

    public static void main(String[] args) {

        Map<User3, String> map = new TreeMap<>(new TreeMapComparator());

 

        map.put(new User3("jimmy1", 30), "hello");

        map.put(new User3("jimmy2", 30), "hello");

        map.put(new User3("jimmy", 22), "hello");

        map.put(new User3("jimmy", 20), "hello");

 

        Set<Entry<User3, String>> entrySet = map.entrySet();

        for (Entry<User3, String> each : entrySet) {

            System.out.println(each.getKey()+"::"+each.getValue());

        }

    }

}

输出结果如下,跟上面的相同。

User3 [username=jimmy, age=20]::hello

User3 [username=jimmy, age=22]::hello

User3 [username=jimmy1, age=30]::hello

User3 [username=jimmy2, age=30]::hello

当然,我们还可以不写比较器类,而是使用匿名内部类的形式来写比较器。

package com.jimmy.map;

 

import java.util.Comparator;

import java.util.Map;

import java.util.Set;

import java.util.TreeMap;

import java.util.Map.Entry;

 

import com.jimmy.domain.User3;

 

public class TreeMapDemo4 {

    public static void main(String[] args) {

        Map<User3, String> map = new TreeMap<>(new Comparator<User3>() {

 

            @Override

            public int compare(User3 o1, User3 o2) {

                int temp = o1.getAge() - o2.getAge();

                return temp == 0 ? o1.getUsername().compareTo(o2.getUsername()) : temp;

            }

        });

 

        map.put(new User3("jimmy1", 30), "hello");

        map.put(new User3("jimmy2", 30), "hello");

        map.put(new User3("jimmy", 22), "hello");

        map.put(new User3("jimmy", 20), "hello");

 

        Set<Entry<User3, String>> entrySet = map.entrySet();

        for (Entry<User3, String> each : entrySet) {

            System.out.println(each.getKey()+"::"+each.getValue());

        }

    }

}

4,一帮以getEntry()方法为基础的获取元素的方法,其中包括containsKey()get()remove()等。

final Entry<K,V> getEntry(Object key) {

        // 如果有自定义比较器对象,就按照自定义规则遍历二叉树

        if (comparator != null)

            return getEntryUsingComparator(key);

        if (key == 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,一帮以getFirstEntry()getLastEntry()为基础的获取头和尾元素的方法,其中包括:firstKey()lastKey()firstEntry()lastEntry()pollFirstEntry()pollLastEntry()

    final Entry<K,V> getFirstEntry() { // 获取第一个元素也就是最小的元素,一直遍历左子树

        Entry<K,V> p = root;

        if (p != null)

            while (p.left != null)

                p = p.left;

        return p;

    }

 

    final Entry<K,V> getLastEntry() { // 获取最后个元素也就是最大的元素,一直遍历右子树

        Entry<K,V> p = root;

        if (p != null)

            while (p.right != null)

                p = p.right;

        return p;

    }

6keySet()entrySet()方法,在将HashMap的时候已经讲过了,Map没有迭代器,要将Map转化为Set,用Set的迭代器才能进行元素迭代。

总结

TreeMap继承了Map的性质,同时其树结构又可以进行元素排序,用处很大。

 

我也不知道自己在哪里看到的,如有侵权立即删除。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值