【JavaSE】TreeSet与TreeMap源码解读

简介: 文章目录1 TreeSet1.1 TreeSet快速入门1.2 TreeSet比较机制源码解读2 TreeMap2.1 TreeMap快速入门2.2 TreeMap比较机制源码解读写在最后

1 TreeSet

1.1 TreeSet快速入门

 相较于HashSet,TreeSet最大的特点是实现了排序,但是需要注意的是,当我们使用无参构造器创建TreeSet时,仍然是无序的。如果希望添加的元素按照字符串大小来排序则需要使用比较器Comparator,并指定排序规则。

来看下面的例子,可以看出,结果按照字符串大小进行了排序:

import java.util.Comparator;
import java.util.TreeSet;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).compareTo((String) o2);
            }
        });
        // 添加数据
        treeSet.add("lucy");
        treeSet.add("drunk");
        treeSet.add("youth");
        treeSet.add("bob");
        treeSet.add("a");
        System.out.println("treeSet = " + treeSet);
    }
}


1.2 TreeSet比较机制源码解读

1️⃣ 构造器把传入的比较器对象,赋给了 TreeSet 底层的 TreeMap 属性。



2️⃣ 在调用 treeSet.add(“drunk”),底层会执行到下述代码,其中cpr 就是我们的匿名内部类(对象),会动态绑定到匿名内部类的compare方法。

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  // 如果相等,即返回0,这个key就没有加入
                    return t.setValue(value);
            } while (t != null);
        }

需要注意的是,当比较结果是相等时,不会将key添加进去!

我们来看下面的例子,尝试将比较规则更改为按照字符串长度排序:

import java.util.Comparator;
import java.util.TreeSet;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o2).length() -((String)o1).length();
            }
        });
        // 添加数据
        treeSet.add("Drunk");
        treeSet.add("Youth");
        System.out.println("treeSet = " + treeSet);
    }
}



由于 Drunk 与 Youth 长度相同,因此,在底层调用比较器的时候,不加入 Youth 这一 key 值。 这也是小白时期常常容易犯的错误,导致了数据的丢失!


2 TreeMap

2.1 TreeMap快速入门

当使用无参构造器的时候,TreeMap默认按照key值进行自然排序。

import java.util.TreeMap;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap();
        // 添加数据
        treeMap.put("drunk", "路宝");
        treeMap.put("abc", "香克斯");
        treeMap.put("cat", "多弗朗明哥");
        System.out.println("treeMap = " + treeMap);
    }
}


也可以传入比较器,进行自定义排序。比如,尝试按照key的字符串长度的大小进行排序:

import java.util.Comparator;
import java.util.TreeMap;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        // 添加数据
        treeMap.put("drunk", "路宝");
        treeMap.put("abc", "香克斯");
        treeMap.put("cat", "多弗朗明哥");
        System.out.println("treeMap = " + treeMap);
    }
}

此时,由于 cat 和 abc 两个键的长度相同,因此,后面的 cat键没有成功加入,只是将 cat 对应的值 与 abc 对应的值进行了替换!





2.2 TreeMap比较机制源码解读

1️⃣ 调用构造器的时候,把传入的实现了 Comparator接口的匿名内部类(对象),赋给了 TreeMap 的属性 comparator;


2️⃣ 在执行第一个put时,加入了一个结点作为root根节点,把 k-v 封装到 Entry对象,放入root;



3️⃣ 之后调用 put 方法,会调用比较器。进行所有 key 的遍历,并动态绑定到匿名内部类的compare()方法。如果发现比当前结点小,则挂到左子树,大了就挂到右子树。如果相等,则只进行value的替换,key不会加入。 使用setValue方法进行。

    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);
        }
相关文章
|
8月前
|
存储 Java uml
|
7月前
|
安全
HashSet(源码解读)
HashSet(源码解读)
29 0
|
8月前
HashSet 源码解读
HashSet 源码解读
33 1
|
8月前
ArrayList源码解读
ArrayList源码解读
33 1
|
8月前
|
存储 安全 Java
Java集合篇之set,面试官:请说一说HashSet、LinkedHashSet、TreeSet的区别?
Java集合篇之set,面试官:请说一说HashSet、LinkedHashSet、TreeSet的区别?
51 0
|
存储 算法 Java
HashSet源码剖析
HashSet源码剖析
69 0
|
存储 安全 Java
【JavaSE】Java基础语法(三十):HashMap与TreeMap
1. HashMap 1.1 HashMap集合概述和特点 HashMap底层是哈希表结构的 依赖hashCode方法和equals方法保证键的唯一 如果键要存储的是自定义对象,需要重写hashCode和equals方法
|
算法 Java
java TreeSet 和 TreeMap 源码解读
java 集合篇章——TreeSet 和 TreeMap 源码解读。
118 0
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比(上)
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比(上)
|
存储 缓存 Java
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比(下)
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比
<Java八股文面试>ArrayList源码 | Iterator源码 | LinkedList和ArrayList对比(下)