标签:
HashSet:先来看HashSet的源码,首先看默认构造器:
public HashSet() {
map = new HashMap<E,Object>();
}
// ok,我们看到构造器中new了一个HashMap。key使用了泛型,value使用Object。 再来看add方法源码:private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// PRESENT是一个Object类型的常量,用来当做map的value. 也就是说,你以后在HashSet中存储的元素都是HashMap中key,value全部使用Object。 HashMap的key是不可以重复的,保证元素唯一的依据是对象的hashCode跟equals方法。public Iterator<E> iterator() {
return map.keySet().iterator();
}通过上面的介绍,已经对HashSet比较了解了,我们知道HashSet底层是用了HashMap。static final int DEFAULT_INITIAL_CAPACITY = 16; // 默认初始化容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;
final float loadFactor; // 用于计算扩容阀值
/* The next size value at which to resize (capacity * load factor) */
int threshold; // Entry扩容阀值
// The table, resized as necessary. Length MUST Always be a power of two.
transient Entry[] table;// 存放键值对的Entry数组
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); // 计算扩容阀值
table = new Entry[DEFAULT_INITIAL_CAPACITY]; // 初始化Entry<K,V>数组
init();
}
/* 在默认构造方法中,初始化了一个容量为16的HashMap(Entry数组),当元素超过75%(16*0.75f=12个)的时候开始自动扩容*/put方法源码: eg: map.put("a","abc");public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode()); // 获取key的hash值
int i = indexFor(hash, table.length); // h & (length-1); 通过hashcode取模数组长度, 定位hash值在table数组中的索引
// 如果table数组中i索引所在位置有元素,循环遍历该链表中的下一个元素
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 遍历到了hash值相同并且equals也相同的key,把value用新值替换掉
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
// table中i索引所在位置没有元素,添加key、value到指定索引处。
addEntry(hash, key, value, i);
return null;
} addEntry()方法源码:void addEntry(int hash, K key, V value, int bucketIndex) {
// 下面两行代码将entry保存进了table数组中Entry内部链表的第一个位置。
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold) // 需要扩容了
resize(2 * table.length); // 重新计算数组大小
} void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor); // 新的扩容阀值
} transfer()方法源码:// 重新分配void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
} 上面是HashMap存元素的实现方式,再来看看取元素的方式:public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode()); // 还是计算key的hashcode,
// 定位hash值在table数组中的索引,并通过equals方法定位元素在链表中的位置。
for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
我们来对HashMap存取元素的过程来做一个小的总结:public class Person {
public int id;
public String name="";
public int hashCode() {
return id;
}
// equals必须比较id
public boolean equals(Person p) {
if(this.id == p.id)
return true;
else
return false;
}
}
标签:
原文地址:http://blog.csdn.net/lisulong1/article/details/52350024