实现序列化接口,表明支持序列化。
)支持克隆(超类克隆) )实现了Cloneable接口,该接口调用方法以指示可以进行浅复制。
继承AbstractSet抽象类,与ArrayList和链接列表一样,他们的抽象父类提供了equals (方法和hashCode )方法。 它们本身没有实现这两种方法。 (但ArrayList和链接列表的equals )实现是不同的。 请看我的ArrayList源代码分析。 (这意味着与HashSet一样从AbstractSet抽象类继承的TreeSet、LinkedHashSet等。 如果元素的数量与集合中的元素相同(即使是AbstractSet的不同子类,也是equals ) )。下面的代码是JDK的equals ) )代码。
从JDK源代码可以看出,基本上,通过调用AbstractCollection的containsAll ) )方法,而不是用我们通常认为的hashcode )方法求出的值进行比较,可以在他们中间
公共布尔型(对象) if ) o==this )返回真; if (! (集实例) )返回假;
collectionc=(collection ) o; //元素的个数必须相等。
if(c.size )!=size () )返回假; 调用了try//abstractcollection的方法。
返回容器(c;
} catch (classcastexceptionunused ) {return false;
} catch (nullpointerexceptionunused ) {return false;
}
}
只要逐一判断publicbooleancontainsall (collection c )//收藏中包含的要素即可。
for(objecte:c ) if (! 连续(e ) )返回假; 返回真;
}
实现了Set接口。
平时的规则是先把总结放在上面:
HashSet内部使用HashMap的键存储集合中的元素,并且内部HashMap的所有值
全部为空。 (向HashSet添加元素时,所有内部HashMap值都是PRESENT ),因此PRESENT直接在实例域位置初始化,不允许更改。
HashSet对外提供的所有方法都是内部通过HashMap操作完成的,要真正了解HashSet的实现,只需了解HashMap的原理即可。 我也分析了关于HashMap的事情。 传送门:
因此,如果你熟悉HashMap,学习HastSet的源代码很容易!
1 (首先,让我们看看HashSet的哪个实例域: (减少了很多,而且不是很简单吗(o )……)。
//序列化ID
staticfinallongserialversionuid=-502474406713321676 l; //基础使用了HashMap存储数据。
隐私转移hashmap图; //用于将值填充到基础数据结构的HashMap中。 因为HashSet只用key保存数据。
私有对象=新对象(;
2 )虽然是往常的规则,但是先看看构造方法吧。
构造函数的实现基本上使用HashMap
//其实只是实例化了HashMap(o )…(不知道的童鞋可以看我关于Hashmap的其他源代码分析)
公共散列()。
map=new HashMap (;
}
//还是
公共散列(collectionextendsec )。
map=newhashmap(math.max((int ) ) c.size )/. 75f (1,16 ) );
addall(c;
//调用的AbstractCollection方法。 然后,调用HashSet的add ) )方法,将集合中的//所有元素添加到集合中。 (因为)
publicbooleanaddall (collectionextendsec ) {
布尔修改=假; for(e:c ) if (添加) e ) )
修改=true; re
turnmodified;}//底层并不支持直接在AbstractCollection类中调用add()方法,而是调用add()方法//的自身实现。
publicboolean add(E e) {throw newUnsupportedOperationException();
}
//初始化HashSet仍然是关于HashMap的知识。
public HashSet(intinitialCapacity) {
map= new HashMap<>(initialCapacity);
}
//初始化HashSet仍然是关于HashMap的知识。
public HashSet(int initialCapacity, floatloadFactor) {
map= new HashMap<>(initialCapacity, loadFactor);
}
//这次初始化底层使用了LinkedHashMap实现。
HashSet(int initialCapacity, floatloadFactor, boolean dummy) {
map= new LinkedHashMap<>(initialCapacity, loadFactor);
}
3:添加新的元素:
//底层仍然利用了HashMap键进行了元素的添加。//在HashMap的put()方法中,该方法的返回值是对应HashMap中键值对中的值,而值总是PRESENT,//该PRESENT一直都是private static final Object PRESENT = new Object();//PRESENT只是初始化了,并不能改变,所以PRESENT的值一直为null。//所以只要插入成功了,put()方法返回的值总是null。
publicboolean add(E e) {return map.put(e, PRESENT)==null;
}
4:删除一个元素:
//该方法底层实现了仍然使用了map的remove()方法。//map的remove()方法的返回的是被删除键对应的值。(在HashSet的底层HashMap中的所有//键值对的值都是PRESENT)
publicboolean remove(Object o) {return map.remove(o)==PRESENT;
}
5:清空方法
public voidclear() {
map.clear();
}
6:克隆方法(浅克隆)
底层仍然使用了Object的clone()方法,得到的Object对象,并把它强制转化为HashSet,然后把它的底层的HashMap也克隆一份(调用的HashMap的clone()方法),并把它赋值给newSet,最后返回newSet即可。
@SuppressWarnings("unchecked")publicObject clone() {try{
HashSet newSet = (HashSet) super.clone();
newSet.map= (HashMap) map.clone();returnnewSet;
}catch(CloneNotSupportedException e) {throw newInternalError(e);
}
}
7:是否包含某个元素
底层仍然使用了HashMap的containsKey()方法(因为HashSet只用到了HashMap的键)同样的话重复了好多遍 ……….(艹皿艹 )
publicboolean contains(Object o) {returnmap.containsKey(o);
}
8:判断是否为空
仍然调用HashMap的方法。
publicboolean isEmpty() {returnmap.isEmpty();
}
9:生成一个迭代器
仍然使用了HashMap的键的迭代器
public Iteratoriterator() {returnmap.keySet().iterator();
}
10: 统计HashSet中包含元素的个数
还是调用HashMap的实现。
public intsize() {returnmap.size();
}
11:把HashSet写入流中(也就是序列化)
在HashSet序列化实现中,仍然是把HashMap中的属性写入流中。(因为HashSet中真正存储数据的数据结构是HashMap。)
private voidwriteObject(java.io.ObjectOutputStream s)
throws java.io.IOException {//Write out any hidden serialization magic
s.defaultWriteObject();//Write out HashMap capacity and load factor//把HashMap的容量写入流中。
s.writeInt(map.capacity());//把HashMap的装载因子写入流中。
s.writeFloat(map.loadFactor());//把HashMap中键值对的个数写入流中。
s.writeInt(map.size());//按正确的顺序把集合中的所有元素写入流中。
for(E e : map.keySet())
s.writeObject(e);
}
12:从流中读取数据,组装HashSet(反序列化)
private voidreadObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {//Read in any hidden serialization magic
s.defaultReadObject();//Read capacity and verify non-negative.
int capacity =s.readInt();if (capacity < 0) {throw new InvalidObjectException("Illegal capacity:" +capacity);
}//Read load factor and verify positive and non NaN.
float loadFactor =s.readFloat();if (loadFactor <= 0 ||Float.isNaN(loadFactor)) {throw new InvalidObjectException("Illegal load factor:" +loadFactor);
}//Read size and verify non-negative.
int size =s.readInt();if (size < 0) {throw new InvalidObjectException("Illegal size:" +size);
}//Set the capacity according to the size and load factor ensuring that//the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);//HashMap中构建哈希桶数组是在第一个元素被添加的时候才构建,所以在构建之前检查它,//调用HashMap.tableSizeFor来计算实际分配的大小,//检查Map.Entry []类,因为它是最接近的公共类型实际创建的内容。
SharedSecrets.getJavaOISAccess()
.checkArray(s, Map.Entry[].class,HashMap.tableSizeFor(capacity));//创建HashMap。
map = (((HashSet>)this) instanceof LinkedHashSet ?
new LinkedHashMap(capacity, loadFactor) :new HashMap(capacity, loadFactor));//按写入流中的顺序再把元素依次读取出来放到map中。
for (int i=0; i
@SuppressWarnings("unchecked")
E e=(E) s.readObject();
map.put(e, PRESENT);
}
}