文章目录
HashSet
、
LinkedHashSet
和
TreeSet
都是 Java 中实现 Set 接口的类,它们有一些共同点,比如都不允许重复元素,但它们的实现方式和行为有所不同。
HashSet
1. HashSet
常用方法
1. add(E e)
add(E e)
方法将指定元素添加到 HashSet
中。如果该元素已经存在,则不做任何操作。
HashSet<String> set = new HashSet<>();
set.add("apple"); // 添加元素 "apple"
set.add("banana"); // 添加元素 "banana"
set.add("apple"); // "apple" 已存在,不会添加
2. remove(Object o)
remove(Object o)
方法用于从 HashSet
中移除指定的元素。如果该元素存在,则将其删除;如果不存在,则返回 false
。
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("cherry");
set.remove("banana"); // 移除元素 "banana"
3. contains(Object o)
contains(Object o)
方法检查 HashSet
是否包含指定的元素。如果存在,则返回 true
;否则返回 false
。
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
System.out.println(set.contains("apple")); // true
System.out.println(set.contains("cherry")); // false
4. size()
size()
方法返回 HashSet
中元素的数量。
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
System.out.println(set.size()); // 输出: 2
5. isEmpty()
isEmpty()
方法检查 HashSet
是否为空。如果为空,则返回 true
,否则返回 false
。
HashSet<String> set = new HashSet<>();
System.out.println(set.isEmpty()); // true
set.add("apple");
System.out.println(set.isEmpty()); // false
6. clear()
clear()
方法从 HashSet
中移除所有元素,使集合变为空。
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.clear(); // 移除所有元素
System.out.println(set); // 输出: []
7. toArray()
toArray()
方法将 HashSet
中的所有元素转换为数组。
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
Object[] array = set.toArray();
for (Object obj : array) {
System.out.println(obj); // 输出: apple, banana
}
2.底层实现
HashSet
使用一个HashMap
来保存数据
transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
- 默认会创建一个容量为16, 加载因子为0.75的HashMap
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
- 添加元素时调用HashMap的put方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
- 当HashMap的
m a p . s i z e ( ) > t h r e s h o l d = m a p . c a p a c i t y ∗ l o a d F a c t o r map.size() > threshold = map.capacity * loadFactor map.size()>threshold=map.capacity∗loadFactor
触发扩容,将capacity
和threshold
扩容为原来的2倍
LinkedHashSet
1. LinkedHashSet
常用方法
LinkedHashSet
继承自 HashSet
,并且保持插入顺序。它的内部使用双向链表来维护元素的顺序,因此可以按插入顺序遍历。
与 HashSet
相比,LinkedHashSet
的唯一不同是它能够保持插入元素的顺序。
LinkedHashSet<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("apple");
linkedSet.add("banana");
linkedSet.add("cherry");
linkedSet.remove("banana");
System.out.println(linkedSet); // [apple, cherry]
2. 底层实现
LinkedHashSet
继承自HashSet
,其构造方法最终创建一个LinkedHashMap
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
- 而
LinkedHashMap
创建一个HashMap
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
LinkedHashSet
在父类的基础上添加了head
和tail
两个字段以实现双向链表
/**
* The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
TreeSet
1. TreeSet
构造方法
TreeSet<Integer> treeSet = new TreeSet<>(); // 无参构造
TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>() { // 指定比较器
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
而对于自定义的对象可以实现Comparable
接口,实现自定义的比较器
public class Exercise {
public static void main(String[] args) {
Person person1 = new Person("aaaa");
Person person2 = new Person("bbb");
Person person3 = new Person("aa");
TreeSet<Person> treeSet = new TreeSet<>();
treeSet.add(person1);
treeSet.add(person2);
treeSet.add(person3);
System.out.println(treeSet); // [Person{name='aa'}, Person{name='bbb'}, Person{name='aaaa'}]
}
}
class Person implements Comparable<Person> {
public String name;
Person(String name){
this.name = name;
}
@Override
public int compareTo(Person o) {
return this.name.length() - o.name.length();
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + '}';
}
}
当类型实现了Comparable
接口的同时,指定了TreeSet
的比较器优先使用比较器
2. TreeSet
常用方法
TreeSet
是一个基于红黑树实现的集合,它能够自动对元素进行排序(默认按照元素的自然顺序,或者通过提供自定义的 Comparator
进行排序)。与 HashSet
和 LinkedHashSet
不同,TreeSet
不允许插入 null
元素。
1. first()
— 返回集合中的第一个元素
first()
方法返回集合中的第一个元素(即最小的元素),该方法依赖于集合的自然顺序或自定义的 Comparator
。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
System.out.println(set.first()); // 输出: 5(集合中的最小元素)
2. last()
— 返回集合中的最后一个元素
last()
方法返回集合中的最后一个元素(即最大的元素)。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
System.out.println(set.last()); // 输出: 20(集合中的最大元素)
3. lower(E e)
— 返回小于指定元素的最大元素
lower(E e)
方法返回小于指定元素 e
的最大元素。如果不存在小于 e
的元素,则返回 null
。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
System.out.println(set.lower(15)); // 输出: 10(小于15的最大元素)
System.out.println(set.lower(5)); // 输出: null(没有小于5的元素)
4. higher(E e)
— 返回大于指定元素的最小元素
higher(E e)
方法返回大于指定元素 e
的最小元素。如果不存在大于 e
的元素,则返回 null
。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
System.out.println(set.higher(15)); // 输出: 20(大于15的最小元素)
System.out.println(set.higher(20)); // 输出: null(没有大于20的元素)
5. headSet(E toElement)
— 返回小于指定元素的所有元素的子集
headSet(E toElement)
方法返回一个子集,该子集包含所有小于指定元素 toElement
的元素。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
set.add(15);
TreeSet<Integer> headSet = (TreeSet<Integer>) set.headSet(15);
System.out.println(headSet); // 输出: [5, 10](小于15的元素)
6. tailSet(E fromElement)
— 返回大于等于指定元素的所有元素的子集
tailSet(E fromElement)
方法返回一个子集,该子集包含所有大于等于指定元素 fromElement
的元素。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
set.add(15);
TreeSet<Integer> tailSet = (TreeSet<Integer>) set.tailSet(15);
System.out.println(tailSet); // 输出: [15, 20](大于等于15的元素)
7. subSet(E fromElement, E toElement)
— 返回指定范围内的所有元素的子集
subSet(E fromElement, E toElement)
方法返回一个子集,该子集包含从 fromElement
到 toElement
(不包括 toElement
)之间的所有元素。
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(20);
set.add(5);
set.add(15);
TreeSet<Integer> subSet = (TreeSet<Integer>) set.subSet(10, 20);
System.out.println(subSet); // 输出: [10, 15](从10到20之间的元素,不包括20)
遍历方式
1. 迭代器
public static void main(String[] args) {
Collection<String> c = new HashSet<>();
c.add("A");
c.add("B");
c.add("C");
for (Iterator<String> it = c.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
2. 增强的for
public static void main(String[] args) {
Collection<String> c = new HashSet<>();
c.add("A");
c.add("B");
c.add("C");
for (String s : c) {
System.out.println(s);
}
}
本质是基于迭代器实现的
public class Exercise {
public static void main(String[] args) {
Student s = new Student("hello world");
for (Character c : s) {
System.out.println(c);
}
}
}
class Student implements Iterable<Character>{
public String name = "Hello world";
@Override
public Iterator<Character> iterator() {
return new Iterator<>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < Student.this.name.length();
}
@Override
public Character next() {
return Student.this.name.charAt(index++);
}
};
}
}
3. lambda
表达式
public static void main(String[] args) {
Collection<String> c = new HashSet<>();
c.add("A");
c.add("B");
c.add("C");
c.forEach(System.out::println);
}
本质是对于匿名内部类的简写,c.forEach(System.out::println);
等价于以下几种写法:
- 匿名内部类
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
lambda
表达式
c.forEach(s -> System.out.println(s));
- 函数引用
c.forEach(System.out::println);