一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2)
的元素对 e1
和 e2
,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
注:如果将可变对象用作 set 元素,那么必须极其小心。如果对象是 set 中某个元素,以一种影响 equals
比较的方式改变对象的值,那么 set 的行为就是不确定的。此项禁止的一个特殊情况是不允许某个 set 包含其自身作为元素。
AbstractSet
此抽象类重写AbstractCollection的任何方法,只是单独的增加了equals
和 hashCode
的实现
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
protected AbstractSet() {
}
// 比较指定对象与此 set的相等性,如果指定的对象也是一个set,并且两个set有相同的大小,元素,则返回true。
public boolean equals(Object o) {
if (o == this)
return true;
//1.先检查指定的对象是不是set
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
//2.再检查指定set的大小与这个set的大小是否相等
if (c.size() != size())
return false;
try {
//3.最后检查这个set是否包含指定set中的所有元素
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
//返回此set的哈希码值:即该set中所有元素的哈希值之和
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
//从此 set中移除包含在指定 collection中的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
//如果此set的大小>指定集合c的大小
if (size() > c.size()) {
//迭代指定集合c中的元素,并在此set中逐个删除
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;
}
}
HashSet
概述
HashSet实现Set接口,由哈希表(实际上是一个HashMap
实例)支持。主要具有以下的特点:
-
不保证set的迭代顺序,特别是它不保证该顺序恒久不变
-
有且只允许一个
null
元素 -
不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();
-
非同步的。如果多 个线程同时访问一个哈希set,而其中至少一个线程修改了该 set,那么它必须保持外部同步。这通常是通过对自然封装该set的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该set进行意外的不同步访问:
Set s = Collections.synchronizedSet(new HashSet());
- HashSet通过iterator()返回的迭代器是fail-fast的
类继承实现关系
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
类属性
//hashMap
private transient HashMap<E,Object> map;
//HashMap的value
private static final Object PRESENT = new Object();
构造方法
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
//不对外公开的一个构造方法(默认default修饰),底层构造的是LinkedHashMap,dummy只是一个标示参数,无具体意义。 给LinkedHashSet调用的
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
其他的所有方法都是调用的HashMap的方法。只需要知道HashSet的特点就可以了。
LinkedHashSet
特点和HashSet一样。基于LinkedHashMap的双向链表。构造方法都是调用HashSet那个默认的构造方法,创建的是LinkedHashMap。有序,有且只有一个null,不允许重复,线程不安全