Set集合接口
概述
public interface Set<E> extends Collection<E>{
//... ...
}
- Package:java.util
- Statement:Interface Set
- Param Type :E - 由此集合维护的元素的类型
- All Superinterfaces:Collection , Iterable
- All Known Subinterfaces:NavigableSet , SortedSet
- All Known Implement Class:AbstractSet HashSet TreeSet EnumSet LinkedHashSetCopyOnWriteArraySetConcurrentSkipListSet ,JobStateReasons ConcurrentHashMap.KeySetView
- Discription:不包含重复元素的集合
注意!
Set
集合不包含重复元素Set
集合中的元素是无序的,即添加时元素时的顺序和遍历时的顺序没任何关系。
HashSet
源码👇
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable,Serializable
{
static final long serialVersionUID = -5024744406713321676L;
//存放元素
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
// ... ...
}
常用方法
添加元素
# add
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashSet
的add
方法实际上是将元素作为 Map
的key值存到Map映射中,Map的value保存了一个默认值。由于Map
的key值是不可重复的,所以HashSet
实现不重复还是依靠Map
映射表中,key不重复的hash算法。
map在put一个键值对的时候会先计算key的hashCode值来判断元素插入的位置,同时也会和其他元素的hashcode值作比较,要是有相近的,再用
equals()
对比内容是否相同,若相同,则认为这个key是重复的,不允许插入。
注意!
HashSet
对于自己创建的类,需要重写hashCode()
,和equals()
方法,对基本类型的数据和字符串类型已经重写了hashCode
方法,和equals()
方法。🔗
# addAll
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
addAll
方法是调用了分类AbstractSet
的addAll
方法,实际的处理逻辑是遍历集合调用add
方法挨个添加。
删除元素
# void clear()
源码👇
public void clear() {
map.clear();
}
# boolean remove(Object o)
源码👇
public boolean remove(Object o) {
return map.remove(o)==PRESENT;//调用了map的删除方法
}
# boolean removeAll(Collection<?> c)
//source:AbstractSet.removeAll
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
if (size() > c.size()) {
for (Iterator<?> i = c.iterator(); i.hasNext(); )
modified |= remove(i.next());
} else {
for (Iterator<?> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
}
return modified;
}
# boolean removeIf(Predicate<? super E> filter)
源码👇
//source:Collection.removeIf
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
修改元素
注意!
- hashSet未提供修改元素的方法,若需要修改元素,先remove再add。
HashSet总结
HashSet
不是线程安全的。HashSet
不能保证插入元素的顺序。HashSet
允许存在null
元素
TreeSet
认识TreeSet
继承关系👇
java.lang.Object
↳ java.util.AbstractCollection<E>
↳ java.util.AbstractSet<E>
↳ java.util.TreeSet<E>
构造方法
# TreeSet()
无参构造方法。
public TreeSet() {
this(new TreeMap<E,Object>());
}
无参的构造方法指定了NavigableMap
接口的实现类,实际又调用了有参的构造方法,初始化了NavigableMap<E,Object> m;
# TreeSet(NavigableMap<E,Object> m)
含参构造方法,可指定一个NavigableMap
接口的实现类。用来初始化NavigableMap<E,Object> m;
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
!
TreeSet
支持个性化拓展,可以自己写个类实现NavigableMap
接口,然后初始化的时候将这个类传进去。
常用方法
添加元素
# boolean add(E e)
将指定元素添加到该集合,如果该元素在集合中不存在。
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
Demo👇
TreeSet<String> strTree = new TreeSet<>();
strTree.add("a");
strTree.add("b");
strTree.add("c");
Iterator<String> iterator = strTree.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
//print:遍历出的顺序和插入时的顺序一样
a
b
c
# boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合中。
源码👇
public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
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);
}
删除元素
# void clear()
从此集合中删除所有元素。
源码👇
public void clear() {
m.clear();
}
# boolean remove(Object o)
如果存在,则从该集合中删除指定的元素。
public boolean remove(Object o) {
return m.remove(o)==PRESENT;//调用TreeMap的remove方法
}
Demo👇
TreeSet<String> strTree = new TreeSet<>();
// ... ...
strTree.remove("a");
Iterator<String> iterator = strTree.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);// b c
}
查找元素
# boolean contains(Object o)
如果此集合包含指定的元素,则返回 true 。
public boolean contains(Object o) {
return m.containsKey(o);//调用TreeMap的getEntry方法
}
Demo👇
TreeSet<String> strTree = new TreeSet<>();
// ... ...
boolean a = strTree.contains("a");
System.out.println(a);//true
TreeSet总结
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
//可导航map的接口,由treeMap实现
//底层还是由TreeMap实现的
private transient NavigableMap<E,Object> m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
TreeSet
是一个有序的集合,它的作用是提供有序的Set集合,插入元素的顺序和遍历元素的顺序一致。- 它继承于
AbstractSet
抽象类,实现了NavigableSet<E>
,Cloneable
,java.io.Serializable
接口。 TreeSet
继承于AbstractSet
,所以它是一个Set
集合,具有Set
的属性和方法。TreeSet
实现了NavigableSet
接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。TreeSet
实现了Cloneable
接口,意味着它能被克隆。TreeSet
实现了java.io.Serializable
接口,意味着它支持序列化。- ⭐
TreeSet
是基于TreeMap
实现的。可以看作是对做了进一步封装。 - ⭐
TreeSet
中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet
时提供的Comparator
进行排序。这取决于使用的构造方法。 TreeSet
为基本操作(add
、remove
和contains
)提供受保证的 log(n) 时间开销。- ⭐
TreeSet
是非同步的。 它的iterator
方法返回的迭代器是fail-fast的。 - ⭐
TreeSet
中的元素不需要重写hashCode()
和equals()
方法,因为TreeSet
是通过比较器去重的,所有元素都必须实现Comparable
接口,然后重写compareTo()
方法。
附录
该博客会基于日常积累,不定时更新或添加内容。