HashSet的实现
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public int size() {
return map.size();
}
HashMap详解
简单来说,HashMap是一个数组加链表的存储结构。添加的时候先将元素放到bucket当中,当key冲突的时候会通过链表链接起来。查找的时候先计算index去查找,第一次没有命中就去遍历链表,直到找到为止。HaspMap是无序的,即遍历结果的次序可能和插入的次序不同。要想遍历的时候按插入次序输出,请使用LinkedHashMap(链表结构)。
两个重要的参数
在HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)。简单的说,Capacity就是bucket的大小,Load factor就是bucket填满程度的比例。如果对迭代性能要求很高的话不要把capacity设置过大,也不要把load factor设置过小。当bucket中的entries的数目大于capacity * load factor时就需要调整bucket的大小为当前的2倍。
put方法的实现
put函数大致的思路为:
-
对key的hashCode()做hash,然后再计算index;
-
如果没碰撞直接放到bucket里;
-
如果碰撞了,以链表的形式存在bucket后面;
-
如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树;
-
如果节点已经存在就替换old value(保证key的唯一性);
-
如果bucket满了(超过load factor*current capacity),就要resize。
get方法的实现
get的大致思路如下:
-
bucket里的第一个节点,直接命中;
-
如果有冲突,则通过key.equals(k)去查找对应的entry;
-
若为树,则在树中通过key.equals(k)查找,时间复杂度为O(logn);
-
若为链表,则在链表中通过key.equals(k)查找,时间复杂度为O(n)。
resize的实现
当put时,如果发现目前的bucket占用程度已经超过了Load Factor所希望的比例,那么就会发生resize。在resize的过程,简单的说就是把bucket扩充为2倍,之后重新计算index,把节点再放到新的bucket中。resize的注释是这样描述的:
Initializes or doubles table size. If null, allocates in accord with initial capacity target held in field threshold. Otherwise, because we are using power-of-two expansion, the elements from each bin must either stay at same index, or move with a power of two offset in the new table.
大致意思就是说,当超过限制的时候会resize,然而又因为我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。
更多详情:Java HashMap的工作原理及实现