目录
一、TreeSet基本介绍
二、TreeSet核心方法
三、TreeSet排序方法
四、TreeSet源码解析
1.无参构造时,底层是创建TreeMap对象
2.有参构造时,底层也创建TreeMap对象
3.执行add方法
4.执行put方法
一、TreeSet基本介绍
- TreeSet是 Java 集合框架中的一个有序集合类,其底层实现主要依赖于红黑树(Red-Black Tree)这一数据结构
- TreeSet的底层是通过TreeMap实现的
- 当我们使用无参构造器,创建TreeSet时,仍然是无序的
- 如果希望添加的元素,按照字符串大小来排序,使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)并指定排序规则
二、TreeSet核心方法
- add(E e):向TreeSet中添加一个元素。如果元素已经存在,则不会添加该元素,并返回false;否则,添加元素并返回true。
- remove(Object o):从TreeSet中移除一个元素。如果元素存在,则移除该元素并返回true;否则,返回false。
- contains(Object o):检查TreeSet是否包含指定的元素。如果包含,则返回true;否则,返回false。
- size():返回TreeSet中元素的数量。
- isEmpty():检查TreeSet是否为空。如果为空,则返回true;否则,返回false。
- first():返回TreeSet中的第一个元素(即最小的元素)。如果TreeSet为空,则抛出NoSuchElementException异常。
- last():返回TreeSet中的最后一个元素(即最大的元素)。如果TreeSet为空,则抛出NoSuchElementException异常。
- headSet(E toElement):返回一个视图,该视图包含小于指定元素的元素。这个视图是TreeSet的一个子集合,对原TreeSet的修改会影响这个视图,反之亦然。
- tailSet(E fromElement):返回一个视图,该视图包含大于或等于指定元素的元素。这个视图同样是TreeSet的一个子集合。
- subSet(E fromElement, E toElement):返回一个视图,该视图包含从指定起始元素(包括)到指定结束元素(不包括)之间的元素。这个视图也是TreeSet的一个子集合。
三、TreeSet排序方法
public class TreeSet_ {public static void main(String[] args) {// 默认排序:无序TreeSet treeSet = new TreeSet();// [a, jack, marry, rose, tom]// 按照字符从小到大排序TreeSet treeSet = new TreeSet((o1, o2) -> ((String) o1).compareTo((String) o2));// [a, jack, marry, rose, tom]// 按照字符从大到小排序TreeSet treeSet = new TreeSet((o1, o2) -> ((String) o2).compareTo((String) o1));// [tom, rose, marry, jack, a]// 按照长度从小到大排序TreeSet treeSet = new TreeSet((o1, o2) -> (((String) o1).length()) - (((String) o2).length()));// [a, tom, jack, marry]// 按照长度从大到小排序TreeSet treeSet = new TreeSet((o1, o2) -> (((String) o2).length())-(((String) o1).length()));// [marry, jack, tom, a]/*这个比较器会计算两个字符串 o1 和 o2 的长度差,并返回这个差值作为比较结果。如果结果为负数,则 o1 被认为小于 o2;如果结果为正数,则 o1 被认为大于 o2;如果结果为0,则两个元素相等(但在 TreeSet 中,相等的元素只会被存储一个)。分析添加到 TreeSet 中的字符串:"jack":长度为4"tom":长度为3"rose":长度为4"marry":长度为5"a":长度为1根据比较器的逻辑,这些字符串会被按照长度排序。由于 "jack" 和 "rose" 长度相同(都是4),它们在排序时被认为是相等的。但是,TreeSet 不允许存储重复的元素,因此尽管 "jack" 和 "rose" 内容不同,但由于长度相同,在添加到 TreeSet 时,"rose" 会被视为与已存在的 "jack" 相等,并且不会被添加进去。这就是为什么在遍历 TreeSet 时,"rose" 没有出现的原因。*/// 区分长度相同但内容不同的字符串TreeSet treeSet = new TreeSet((o1, o2) -> {int lengthComparison = Integer.compare(((String) o1).length(), ((String) o2).length());if (lengthComparison != 0) {return lengthComparison;} else {return ((String) o1).compareTo(((String) o2));}});// [a, tom, jack, rose, marry]treeSet.add("jack");treeSet.add("tom");treeSet.add("rose");treeSet.add("marry");treeSet.add("a");System.out.println(treeSet);}
}
四、TreeSet源码解析
1.无参构造时,底层是创建TreeMap对象
public TreeSet() {this(new TreeMap<>());
}
2.有参构造时,底层也创建TreeMap对象
把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator
public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;
}
3.执行add方法
因为TreeSet不关注value,所以会有一个静态常量对象放在value的位置
private static final Object PRESENT = new Object();public boolean add(E e) {return m.put(e, PRESENT)==null;
}3.执行put方法:存放数据
4.执行put方法
public V put(K key, V value) {Entry<K,V> t = root;if (t == null) {compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;// split comparator and comparable pathsComparator<? super K> cpr = comparator;if (cpr != null) { // cpr 就是匿名内部类(对象)do {parent = t;// 动态绑定到匿名内部类(对象)comparecmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else // 如果相等,即返回0,这个Key就没有加入// value仍然是原来的PRESENT来占位return t.setValue(value);} while (t != null);}