Collection中Set家族:
### TreeSet关于他的一些详细特性在Collection集合体系全景图(三)已经详细讲过, > TreeSet底层是采用TreeMap实现的一种Set,所以它是有序的,同样也是非线程安全的。TreeMap 底层数据结构是红黑树TreeSet特点
- 有序性:TreeSet中的元素是有序的,它们按照自然顺序或者指定的比较器顺序进行排序。
- 唯一性:TreeSet中的元素是唯一的,它们不会重复。
- 可排序性:TreeSet中的元素可以按照自然顺序或者指定的比较器顺序进行排序。
- 底层数据结构:TreeSet底层采用红黑树实现,因此插入、删除、查找等操作的时间复杂度为O(log n)。
- 线程不安全:TreeSet不是线程安全的,如果多个线程同时访问一个TreeSet对象,可能会出现并发问题。
- 不允许空元素:TreeSet不允许插入空元素,否则会抛出NullPointerException异常。
使用案例
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// 创建一个TreeSet对象
TreeSet<Integer> set = new TreeSet<>();
// 添加元素
set.add(5);
set.add(2);
set.add(8);
set.add(1);
set.add(10);
// 打印集合元素
System.out.println("TreeSet集合元素:" + set);
// 获取第一个元素
int first = set.first();
System.out.println("第一个元素:" + first);
// 获取最后一个元素
int last = set.last();
System.out.println("最后一个元素:" + last);
// 获取小于等于指定元素的最大元素
int floor = set.floor(6);
System.out.println("小于等于6的最大元素:" + floor);
// 获取大于等于指定元素的最小元素
int ceiling = set.ceiling(6);
System.out.println("大于等于6的最小元素:" + ceiling);
// 获取小于指定元素的最大元素
int lower = set.lower(6);
System.out.println("小于6的最大元素:" + lower);
// 获取大于指定元素的最小元素
int higher = set.higher(6);
System.out.println("大于6的最小元素:" + higher);
// 删除元素
set.remove(5);
System.out.println("删除元素5后的集合元素:" + set);
// 获取集合大小
int size = set.size();
System.out.println("集合大小:" + size);
// 判断集合是否为空
boolean empty = set.isEmpty();
System.out.println("集合是否为空:" + empty);
// 清空集合
set.clear();
System.out.println("清空集合后的元素:" + set);
}
}
TreeSet源码分析
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
它继承了 AbstractSet 类,因此它具有 Set 接口的所有基本功能,如添加、删除、查找等操作。同时,它还实现了 NavigableSet 接口,这意味着它支持一些高级操作,如获取子集、查找最小值和最大值、查找前驱和后继元素等。此外,它还实现了 Cloneable 和 Serializable 接口,这意味着它可以被克隆和序列化。
//用于存储 TreeSet 中的元素。其中,NavigableMap 是一个支持按照排序顺序访问键值对的 Map 接口,
//它扩展了 SortedMap 接口
//transient 关键字表示该字段不会被序列化
private transient NavigableMap<E,Object> m;
//表示一个静态常量,用于作为 NavigableMap中的 value,因为 NavigableMap 的实现是基于 Map 的
//而 Map 中的 value 是可以为 null 的,但是 `TreeSet` 中的元素是不能重复的,
//因此需要一个非 null 的 value 来占位。
private static final Object PRESENT = new Object();
//构造方法,接受一个 NavigableMap类型的参数 m,将其赋值给成员变量 m
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
//无参构造方法,创建一个默认的空的 TreeSet,底层使用 TreeMap 实现
public TreeSet() {
this(new TreeMap<E,Object>());
}
//创建一个新的TreeSet实例,使用指定的Comparator来比较元素,并使用一个新的TreeMap实例来存储元素
//TreeSet是一个有序的集合,它根据元素的自然顺序或者指定的Comparator来排序元素
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
案例教学
// 创建一个按照字符串长度从小到大排序的 TreeSet
TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// 添加元素
set.add("hello");
set.add("world");
set.add("java");
// 输出结果
System.out.println(set); // [java, hello, world]
继续源码分析TreeSet
//这个构造函数是用来创建一个 TreeSet 对象,并将指定集合中的所有元素添加到 TreeSet 中。其中,参数 c 是一个集合,它包含了要添加到 TreeSet 中的元素
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
//用于将一个集合中的所有元素添加到另一个集合中。
//它还检查源集合是否是一个有序集合,并且目标集合是否是一个TreeMap。如果是,则将源集合中的元素添加到目标集合中
//并返回true。如果不是,则调用父类的addAll方法
public boolean addAll(Collection<? extends E> c) {
//Map对象m的大小为0;
// 集合c的大小大于0,且c是SortedSet类型,m是TreeMap类型
//instanceof 是 Java 中的一个关键字,用于判断一个对象是否属于某个类或其子类的实例
//c instanceof SortedSet 判断集合 c是否是 SortedSet类或其子类的实例,
//m instanceof TreeMap 判断映射 m 是否是 TreeMap 类或其子类的实例
//说明集合 c 是有序集合,映射 m 是基于红黑树实现的有序映射。
//SortedSet是Java集合框架中的一个接口,它继承自Set接口,表示一个有序的集合
if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
//用于将一个集合中的所有元素添加到当前集合中
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
TreeSet(Collection<? extends E> c) 的使用案例:
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(2);
TreeSet<Integer> set = new TreeSet<>(list);
System.out.println(set);//输出结果为:[1, 2, 3, 4]
继续源码分析TreeSet
//实现了 SortedSet接口中的 iterator() 方法,返回一个迭代器,
//用于遍历集合中的元素。具体实现是通过调用 TreeMap 的 navigableKeySet() 方法获取一个有序的键集合,然后返回该键集合的迭代器
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
//逆序迭代
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
}
//return m.put(e, PRESENT)==e不存在于 m 中,则将其添加到 m 中,并返回 true,否则不做任何操作并返回 false
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
//该方法返回一个NavigableSet,该集合包含从fromElement到toElement范围内的元素。
//fromInclusive和toInclusive参数指定是否包括fromElement和toElement本身。
//该方法使用TreeMap的subMap方法来实现子集的创建。
//范围取值
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
E toElement, boolean toInclusive) {
return new TreeSet<>(m.subMap(fromElement, fromInclusive,
toElement, toInclusive));
}
//该方法返回一个NavigableSet,其中包含小于(或等于,如果包含toElement)给定元素的所有元素
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new TreeSet<>(m.headMap(toElement, inclusive));
}
//该方法返回一个NavigableSet,其中包含大于(或等于,fromElement)给定元素的所有元素
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new TreeSet<>(m.tailMap(fromElement, inclusive));
}
subSet案例实现
import java.util.NavigableSet;
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
// 创建一个TreeSet
TreeSet<Integer> treeSet = new TreeSet<>();
// 添加元素
treeSet.add(10);
treeSet.add(20);
treeSet.add(30);
treeSet.add(40);
treeSet.add(50);
// 获取子集
NavigableSet<Integer> subSet = treeSet.subSet(20, true, 40, true);
// 输出子集
System.out.println("子集元素:");
for (Integer i : subSet) {
System.out.println(i); //20,30,40
}
}
}
以上就是TreeSet 源码中比较重要的点,其他的一些方法大家可以自行学习探讨
LinkedHashSet特点
- 有序性:LinkedHashSet中的元素是按照插入顺序进行排序的,因此可以保证元素的顺序性。
- 可以避免重复元素:LinkedHashSet中不允许存储重复元素,如果尝试添加重复元素,将会被忽略。
- 性能较好:LinkedHashSet的性能与HashSet相当,但是由于需要维护元素的插入顺序,因此在插入和删除元素时可能会稍微慢一些。
- 底层实现是HashMap:LinkedHashSet的底层实现是一个HashMap,因此具有HashMap的所有特点,如快速查找、高效存储等。
LinkedHashSet源码分析
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
LinkedHashSet 是一个有序的集合,它保留了元素插入的顺序。它继承了 HashSet 的所有方法,并且添加了一些额外的方法来支持有序集合。
//LinkedHashSet类的构造方法,它接受两个参数:initialCapacity
//和loadFactor。initialCapacity表示集合的初始容量,loadFactor表示负载因子
//相当创建一个指定参数HashSet集合
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
//给一个初始容量,负载因子默认扩容参数,0.75,可以容纳75%的元素,然后就需要扩展容量。
//相当创建一个初始值
HashSet集合
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
//给一个初始容量16,负载因子为0.75
public LinkedHashSet() {
super(16, .75f, true);
}
//,它包含从指定集合中获取的元素。它首先调用父类HashSet的构造函数,
//该构造函数将初始容量设置为指定集合大小的两倍或11(取两者中的较大值),负载因子为0.75,
//以及指定LinkedHashSet实例的访问顺序为插入顺序。
//它调用addAll方法将指定集合中的所有元素添加到新的LinkedHashSet中
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
LinkedHashSet案例演示
import java.util.LinkedHashSet;
public class LinkedHashSetExample {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
// 添加元素
set.add("apple");
set.add("banana");
set.add("orange");
set.add("apple"); // 重复元素不会被添加
// 打印元素
System.out.println(set); // 输出 [apple, banana, orange]
// 删除元素
set.remove("banana");
// 判断元素是否存在
System.out.println(set.contains("apple")); // 输出 true
System.out.println(set.contains("banana")); // 输出 false
// 获取元素数量
System.out.println(set.size()); // 输出 2
// 清空集合
set.clear();
System.out.println(set); // 输出 []
}
}