【Java Map】简述TreeSet

一、引出正题

提出需求:
需要对一组数据进行排序,并剔除重复数据。

解决思路:
①:首先你可能会想到使用 List + Comparator,当然这是一种很普遍常用的实现方式。
②:然后在排序的同时剔除重复数据,那这时候你肯定会跟已有的节点做比较(循环比较,或者将节点存放在一个map中再通过containsKey()的方式比较)。

上述解决思路也挺好,那我们能不能换种思路呢?
当然,仔细思考一下,上述思路其实和Java中现有的 TreeSet 还挺像的,那我们何妨不直接拿来使用呢!

二、TreeSet登场

1. 示例

public class TreeSetDemo {
    public static void main(String[] args) {
        int[] nums = { 3, 2, 5, 1, 7, 0, 1 };
        TreeSet<Integer> treeSet = new TreeSet<>();

        for (int i = 0; i < nums.length; i++) {
            treeSet.add(nums[i]);
        }

        System.out.println(treeSet);
    }
}

执行结果:这不就是我们想要的嘛!

[0, 1, 2, 4, 6]

2. 原理分析

TreeSet类图

【Java Map】简述TreeSet

从类图中很直观的就可以看出,TreeSet 是一个支持排序(可以自定义实现排序算法,实现 Comparator)的集合。


TreeSet源码分析

TreeSet.java

  • 添加元素动作
/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;

// 添加元素
// 使用map那很容易的就能联想到不支持重复数据
public boolean add(E e) {
    return m.put(e, PRESENT)==null;	//真正实现在TreeMap中
}

TreeMap.java

  • 判断是否第一次添加,第一次则初始化Entry节点;
  • 非第一次添加则根据Comparator算法,循环比较其所在位置,并建立好左右节点关系;
  • 非重复节点返回null,重复节点则返回虚拟对象(PRESENT);

【Java Map】简述TreeSet

public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {	// 第一次put操作
        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);	// 循环比较排序
    }
    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;
}

Entry的结构:

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;

    /**
     * Make a new cell with given key, value, and parent, and with
     * {@code null} child links, and BLACK color.
     */
    Entry(K key, V value, Entry<K,V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
    }
}

三、总结

在Java世界中,已经封装了非常多非常完善的数据结构API,我们在使用的时候可以多思考,不要仅仅局限于只使用最常规的ArrayListHashMap 这两个最最常见的API。




感谢阅读,下次再见。ヾ( ̄▽ ̄)ByeBye

上一篇:Oracle创建只读账号的详细步骤


下一篇:深入Java集合学习之LinkedHashMap的实现原理