HashSet是基于HashMap实现的,之前有被问到HashSet和HashMap的区别,我只死记了一条:HashSet的存储的内容不可重复,HashMap可以重复.那么HashSet基于HashMap实现,怎么做可以让HashSet中的对象不重复呢?
HashSet源码##
//Hashset类内部维护了一个HashMap类,提供5种构造方法用于实例化map类.
private transient HashMap<E,Object> map;
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);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
HashSet的size,isEmpty,contains,clear,add,remove等方法都是基于HashMap的对应方法实现的,而HashSet的不重复特性依靠add方法
//HashSet被调用的时候生成的一个简单对象
private static final Object PRESENT = new Object();
//调用HashMap的put方法,将要插入的对象作为key,PRESENT作为值,两者联合为键值对存入map中
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//第一步首先判断key的哈希码是否一致,第二步判断代表key的对象是否一致,如果判断都通过的话新的值替换旧的值.
//因此反复地传入相同对象,只会在一个位置重复替换没有实际意义的PRESENT对象(或者说根本没换过),达到了防重复的目的.
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
//该方法在LinkedList有实现.
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//如果无法匹配已经存在的键,则插入一个新的键值对.
addEntry(hash, key, value, i);
return null;
}
- 判断是否重复的一个重要依据是对象的hashCode方法和euqal方法,我们可以通过重写这两个方法来自由制定我们的防重复的规则.
- 例如我们判断对象是否重复基于其中的一个属性
public class Person {
public Integer age = 14;
@Override
public int hashCode() {
return age.hashCode();
}
@Override
public boolean equals(Object obj) {
Person person = (Person) obj;
if(person.age == this.age) {
return true;
}
return false;
}
}
这样两个age相同的Person类就只能有一个被纳入HashSet中