首先,最重要的,HashMap
作为一个我们使用非常多的集合。最常被大家认知的是,它是一个key-value形式存储数据的数据结构,可以实现快速的存,取操作。
关于HashMap的源码,我们截取一部分分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,
Cloneable, Serializable { /** *
The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY
= 1 << 4 ; //
aka 16 /** *
The maximum capacity, used if a higher value is implicitly specified *
by either of the constructors with arguments. *
MUST be a power of two <= 1<<30. */ static final int MAXIMUM_CAPACITY
= 1 << 30 ; /** *
The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR
= 0 .75f; /** *
An empty table instance to share when the table is not inflated. */ static final Entry<?,?>[]
EMPTY_TABLE = {}; /** *
The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry<K,V>[]
table = (Entry<K,V>[]) EMPTY_TABLE; |
我们看到了它实现的接口有Serializable,说明他能序列化,有Cloneable,说明能被克隆,有Map<K,V>,说明是一个Map(废话)。还有几条有用的信息:第一:有个默认初始容量,第二:有个最大容量,第三:有个调节因子,第四:有个数组。
再往后看,发现这玩意又是由一个数组组成的,而这个数组的初始容量是16,最大容量是2^30。当然这个调节因子这个地方暂且不提。
既然是用数组存储,我们存的又是Key-Value形式,这数组是怎么运作的呢?这里用往Map中添加数据为例说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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; if (e.hash
== hash && ((k = e.key) == key || key.equals(k))) { V
oldValue = e.value; e.value
= value; e.recordAccess( this ); return oldValue; } } modCount++; addEntry(hash,
key, value, i); return null ; } |
由于篇幅问题,我们就不粘贴太多代码了,先说一下,我们的hashMap实现是有一个数组实现,数组中存储的都是Entry<Key,Value>节点,Entry是HashMap的内部类,存储的是key,value,key的哈希值和下一个节点,具体可以自己查看源码。
这里我们看到,首先,如果这个数字为空,则先初始化一个数组,如果要添加的key为空,直接进行空Key的添加(这里HashMap是允许有一个key为空的值的)。如果key不为空,则算出key的哈希值,根据哈希值,查找对应数组的位置,如果此key存在,就是说有相同的key,则覆盖(其实是链到后面的串,每个数组元素往后一个链表),没有则直接添加。这么一说其实就简单了,我们查询什么的就可以根据哈希值直接定位到相应的Entry,然后扔出来就是了。
然后我们说说HashSet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class HashSet<E> extends AbstractSet<E> implements Set<E>,
Cloneable, java.io.Serializable { static final long serialVersionUID
= -5024744406713321676L; private transient HashMap<E,Object>
map; //
Dummy value to associate with an Object in the backing Map private static final Object
PRESENT = new Object(); /** *
Constructs a new, empty set; the backing <tt>HashMap</tt> instance has *
default initial capacity (16) and load factor (0.75). */ public HashSet()
{ map
= new HashMap<>(); } |
这里就可以看出,HashSet底层就是一个HashMap,而它跟HashMap不同的就是它是实现Set接口,所以他的数据必须唯一。
最后是HashTable:
由于篇幅问题,就不贴代码了,跟HashMap差不多,底层也是一个数组。就说一下它跟HashMap主要的区别吧:
第一:HashMap允许一个null的key和任意value都有null,而HashTable无论key和value都不能为空。
第二:HashTable可以看做是线程安全的HashMap。