【Java专题】Java中的TreeMap集合详解

一、TreeMap集合的概述

1、什么是TreeMap集合?
 TreeMap集合:他是一个有序的集合,可以任意顺序将元素插入到集合中,对集合进行遍历的时候每个元素将自动按照排好序的顺序输出。他的底层是采用了二叉树对元素进行排序。
 
2、特点
  • TreeMap 是一个有序的key-value集合,它是通过红黑树实现的
  • TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合
  • TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法
  • TreeMap 实现了Cloneable接口 ,意味着它能被克隆
  • TreeMap 实现了java.io.Serializable接口,意味着它支持序列化
 
注意: TreeMap底层是基于红黑树实现的,该映射根据其键的自然顺序进行排序,或根据创建映射时提供的Comparator进行排序,关键在于使用的构造方法的不同。
 
3、TreeMap集合继承结构图
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable{}

 

继承结构图说明:
  • TreeMap实现继承于AbstractMap,并且实现了NavigableMap接口
  • TreeMap的本质是红黑树,它包含几个重要的成员变量: root, size, comparator
 
4、成员变量
private final Comparator<? super K> comparator;

private transient Entry<K,V> root;

private transient int size = 0;

private transient int modCount = 0;

5、构造方法

public TreeMap() {
    comparator = null;
}

public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

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) {
    }
}

 

二、深入TreeMap集合

先看一个简单实例:
/**
* @author Jason
* @create 2020-07-16 9:37
*/
public class TreeMapTest {
  public static void main(String[] args) {
    TreeSet<String> strs = new TreeSet<>();
    strs.add("jason");
    strs.add("jack");
    strs.add("tom");
    strs.add("arm");
    strs.add("bob");
    //升序
    for (String str : strs) {
      System.out.println(str);
    }
  }
}

上面的实例是升序排序,那么它的底层原理是什么呢?

(1)TreeSet集合底层实际上是一个TreeMap集合
(2)TreeMap集合底层是一个二叉树
(3)放到TreeSet集合中的元素,等同于放到了TreeMap集合的key部分了
(4)TreeSet集合中的元素,无序不可重复,但是可以按照元素的大小顺序自动排序(可排序集合)
 
底层源码:
//调用TreeSet无参构造函数
public TreeSet() {
    this(new TreeMap<E,Object>());
}

//TreeSet又调用了TreeMap集合的无参构造函数,并且家那个comparator赋值为空
public TreeMap() {
    comparator = null;
}

//向该集合中添加元素,调用了TreeMap集合的put方法(核心方法),对于comparator为空的情况走else,对key值进行判断,分三种情况(大于0,小于0,等于0)
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;
        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);
    }
    //comparator为空的情况下
    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)
                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;
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

注意:这里是什么类型都可以比较吗?当然不是啦,请看下面的实例,及输出结果。

/**
* @author Jason
* @create 2020-07-16 11:47
*/
public class TreeSetTest01 {
  public static void main(String[] args) {
    com.jason.demo14.Person p1 = new com.jason.demo14.Person(20);
    com.jason.demo14.Person p2 = new com.jason.demo14.Person(19);
    com.jason.demo14.Person p3 = new com.jason.demo14.Person(21);
    com.jason.demo14.Person p4 = new com.jason.demo14.Person(33);
    com.jason.demo14.Person p5 = new com.jason.demo14.Person(18);

    TreeSet<Person> persons = new TreeSet<>();
    persons.add(p1);
    persons.add(p2);
    persons.add(p3);
    persons.add(p4);
    persons.add(p5);

    for (Person person : persons) {
      System.out.println(person);
    }
  }
}

/**
* @author Jason
* @create 2020-07-16 14:38
*/
public class Person {
    int age;
    public Person(int age) {
      this.age = age;
    }

    @Override
    public String toString() {
      return "Person[age=" + age + "]";
    }
}

控制台输出:

Exception in thread "main" java.lang.ClassCastException: com.jason.demo14.Person cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1290)
    at java.util.TreeMap.put(TreeMap.java:538)
    at java.util.TreeSet.add(TreeSet.java:255)
    at com.jason.demo14.TreeSetTest01.main(TreeSetTest01.java:18)

分析原因:上面报异常,原因在于自定义类型的集合底层不知道比较规则,这里需要我们手动的实现Comparable接口,重写Comparator方法。而集合本身出现的String类型或者Integer类型底层都有实现Comparable接口和重写CompareTo方法。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}

public int compareTo(String anotherString) {
}

第一种方法:在集合中的元素手动的实现Comparable接口,重写Comparator方法

/**
* @author Jason
* @create 2020-07-16 14:38
*/
public class Person implements Comparable<Person>{
    int age;
    public Person(int age) {
      this.age = age;
    }

    @Override
    public String toString() {
      return "Person[age=" + age + "]";
    }

  @Override
  public int compareTo(Person o) {
    return o.age-this.age;
  }
}

输出结果:

Person[age=33]
Person[age=21]
Person[age=20]
Person[age=19]
Person[age=18]

第二种方法:在构造器TreeSet或TreeMap集合的时候给他传一个比较器

/**
* @author Jason
* @create 2020-07-16 11:47
*/
public class TreeSetTest01 {
  public static void main(String[] args) {
    Person p1 = new Person(20);
    Person p2 = new Person(19);
    Person p3 = new Person(21);
    Person p4 = new Person(33);
    Person p5 = new Person(18);

    TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
      @Override
      public int compare(Person o1, Person o2) {
        return o1.age-o2.age;
      }
    });
    persons.add(p1);
    persons.add(p2);
    persons.add(p3);
    persons.add(p4);
    persons.add(p5);

    for (Person person : persons) {
      System.out.println(person);
    }
  }
}

/**
* @author Jason
* @create 2020-07-16 14:38
*/
public class Person {
    int age;
    public Person(int age) {
      this.age = age;
    }

    @Override
    public String toString() {
      return "Person[age=" + age + "]";
    }
}

输出结果:

Person[age=18]
Person[age=19]
Person[age=20]
Person[age=21]
Person[age=33]

 

 
 
 
 
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术蜗牛-阿春

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值