HashSet是Java中经常被用到的集合,弄清它的底层实现,有利于我们更好地使用它,又是这句话….. 不过HashSet相对于其他集合框架简单了不少。
老规矩,还是先看看它的UML图:
①:实现了Serializable接口,表明它支持序列化。
②:实现了Cloneable接口,表明它支持克隆,可以调用超类的clone()方法进行浅拷贝。
③继承了AbstractSet抽象类,和ArrayList和LinkedList一样,在他们的抽象父类中,都提供了equals()方法和hashCode()方法。它们自身并不实现这两个方法,(但是ArrayList和LinkedList的equals()实现不同。你可以看我的关于ArrayList这一块的源码解析)这就意味着诸如和HashSet一样继承自AbstractSet抽象类的TreeSet、LinkedHashSet等,他们只要元素的个数和集合中元素相同,即使他们是AbstractSet不同的子类,他们equals()相互比较的后的结果仍然是true。下面给出的代码是JDK中的equals()代码:
从JDK源码可以看出,底层并没有使用我们常规认为的利用hashcode()方法求的值进行比较,而是通过调用AbstractCollection的containsAll()方法,如果他们中元素完全相同(与顺序无关),则他们的equals()方法的比较结果就为true。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
//必须保证元素的个数相等。
if (c.size() != size())
return false;
try {
//调用了AbstractCollection的方法。
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
public boolean containsAll(Collection<?> c) {
//只需要逐个判断集合是否包含其中的元素。
for (Object e : c)
if (!contains(e))
return false;
return true;
}
④实现了Set接口。
老规矩还是先把总结放到上面:
①HashSet内部通过使用HashMap的键来存储集合中的元素,而且内部的HashMap的所有值
都是null。(因为在为HashSet添加元素的时候,内部HashMap的值都是PRESENT),而PRESENT在实例域的地方直接初始化了,而且不允许改变。
②HashSet对外提供的所有方法内部都是通过HashMap操作完成的,所以,要真正理解HashSet的实现,只需要把HashMap的原理理解即可。我也对HashMap做了分析。传送门:
③所以,如果你对HashMap很熟悉的话,学习HastSet的源码轻而易举!