Java集合(十二)TreeMap解读

发布于:2022-11-28 ⋅ 阅读:(279) ⋅ 点赞:(0)

TreeSet的底层是TreeMap。

public boolean add(E e) {
        return m.put(e, PRESENT)==null;
//其中的e为我们放进去的元素,作为key来存放的。
//而后面的value为PRESENT为一个固定的值,
    }

而其中的PRESENT是在treeSet里面创建的静态的final类型的Object.

private static final Object PRESENT = new Object();

如果我们使用TreeMap,里面放置的是(e为Key,PRESENT为对应的那一个值)。

我们通过具体的代码设计如下所示:

package com.rgf.map;

import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        TreeMap treeMap = new TreeMap();
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

}

运行界面如下所示:

 我们查看如下所示:

我们发现TreeMap类里面除了提供无参的类以外,也有TreeMap(Comparator <K>)这样子的构造器。TreeMap可以传入一个实现了 Comparator接口的一个匿名内部类,匿名内部类里面我们仍然可以去指定添加我们的键值对的这种排序规则。

我们设计排序规则进行如下代码所示:

package com.rgf.map;

import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。
        
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的大小进行排序。
                return ((String) o1).compareTo((String)o2);
            }
        });
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("Kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

我们运行之后如下所示:

以上排序为从小到大,我们可以将排序修改为从大到小,我们进行修改代码如下所示:

package com.rgf.map;

import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。

        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的大小进行排序。(从小到大)
                return ((String) o2).compareTo((String)o1);
            }

        }) ;


        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

运行界面如下所示:

 

我们可以按照长度进行排序(从小到大),我们将代码修改如下所示:

package com.rgf.map;

import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。

        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的长度大小进行排序。(从小到大)
                return ((String) o1).length()-((String) o2).length();
            }

        }) ;


        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

我们的运行界面如下所示:

 我们也可以将排序找到长度从大到小,我们将代码修改如下所示:

package com.rgf.map;

import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap,是无序的,是指输入输出的顺序不一致
        //我们按照传入的k(String)的大小进行排序。

        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //我们按照传入的k(String)的长度大小进行排序。(从小到大)
                return ((String) o2).length()-((String) o1).length();
            }

        }) ;


        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kristina","克瑞斯提诺");
        treeMap.put("smith","斯密斯");
        System.out.println("treeMap="+treeMap);
    }
}

运行界面如下所示:

 我们来进行Debug,我们从如下代码开始进行debug:

TreeMap treeMap = new TreeMap(new Comparator()

来进行分析源码,如下所示:

1.构造器,把传入的实现了Comparator接口的匿名内部类(对象),传给了TreeMap的comparator属性。

 //他的底层仍然是TreeMap,把传入的匿名内部类comparator赋给TreeMap的一个属性this.comparator.
 public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

我们退出去之后,进去put方法:

2.调用put方法

2.1第一次添加,把k-v封装到Entry对象,放入root.

 源码如下所示;

public V put(K key, V value) {
        Entry<K,V> t = root;//此时root为空,将root赋值给t.
//我们第一次进去,所以还没有进行初始化,第一次初始化为空。
        if (t == null) {//此时t为空,从而进入里面代码。
            compare(key, key); // type (and possibly null) check
//比较key值是否相同,构造器判断规则不同,key值不同,当前规则为比较长度大小。则长度为key.
//TreeMap底层为Entry,Entry为TreeMap里面的一个内部类。
            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)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
     

我们进行第一次添加之后,我们如下所示:

2.2以后添加,即要使用比较器进行比较后添加。

此时root里面已经有值了。此时添加第一个值得时候,没有添加比较器。我们继续进行添加,查看源码。

public V put(K key, V value) {
        Entry<K,V> t = root;
//由于此前已经有一个值了,此时root不为null.t也不为空,即不再进入下面第一个if语句。
        if (t == null) {
//第一次进去也调用了compare方法,即动态绑定了我们的匿名内部类的compare方法,传入的是两个key,即第一次添加的key加了两份进去。目的是为了判定是否为null值。
            compare(key, key); // type (and possibly null) check

            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;//比较器传入的值赋值给cpr
        if (cpr != null) {
            do { //遍历所有的key,给当前key找到适当的位置
                parent = t; //我们传入的comparator赋值给parent
                cmp = cpr.compare(key, t.key); //动态绑定到我们的匿名内部类的compare方法。
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else  //如果遍历过程中,发现准备添加key和当前已有的key相等,就不添加了,但是会进行覆盖原来的值。是由我们自定义的compare方法来决定的。
                    return t.setValue(value);
            } while (t != null);
        }
      

 我们会动态绑定到我们的匿名内部类:

我们调用了compare方法了:

 我们继续往进走:

  final int compare(Object k1, Object k2) {
//如果comparator不等于null的时候,我们进行动态绑定到我们的匿名内部类的compare方法。
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }

 我们会动态绑定到我们的匿名内部类:

此时的o1和o2都是jack.但是这个结果对是否添加当前的value值有任何影响。主要是为了检测是否为null。如果为null,则会在comparator方法里面抛出异常。

这里的compare作用是查看key是否为空,还有就是判断当前key是否实现了compareable接口是否是可以进行比较的。


网站公告

今日签到

点亮在社区的每一天
去签到