Map主要学习HashMap,TreeMap,HashTable
HashMap
因为HashMap是基于hash表实现的,所以不会hash数据结构的可以参考如下链接,hashMap是采用的链地址法来解决冲突
以下面一段代码来分析hashMap源码
public void Test(){ HashMap<String,String> hashMap = new HashMap<String, String>(); hashMap.put("1", "m"); hashMap.put("2", "a"); hashMap.put("3", "p"); System.out.println(hashMap.get("1")); }
构造函数:
public HashMap(int initialCapacity, float loadFactor) { //检测参数,loadFactor表示装载因子,因为HashMap采用的链地址法,所以装载因子可以是1, // 但是会影响性能,默认选择为0.75 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; //该map中装载的最大数量 threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); //创建数组 table = new Entry[capacity]; useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); init(); }
put(K key, V value)
首先查找map中有没该元素,如果有,则直接替换其值,如果没有找到,则通过addEntry(int hash, K key, V value, int bucketIndex)插入数据,把这个值插入到链表的头部
public V put(K key, V value) { if (key == null)//插入key为null的值 return putForNullKey(value); int hash = hash(key);//获得hash值 int i = indexFor(hash, table.length);//获得在table中的索引 //迭代查找该元素在链表中要插入的位置 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //如果该map中已经有该key存在,那么替换其值即可 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; }
public V get(Object key) { if (key == null) return getForNullKey(); //通过getEntry来查找 Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); }
final Entry<K,V> getEntry(Object key) { int hash = (key == null) ? 0 : hash(key);//得到hash值 //遍历此节点位置的链表 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 != null && key.equals(k)))) return e; } return null; }
remove(Object key)
public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); }
final Entry<K,V> removeEntryForKey(Object key) { //获取hash值 int hash = (key == null) ? 0 : hash(key); //得到其索引 int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; //遍历整个链表 while (e != null) { Entry<K,V> next = e.next; Object k; //找到要删除的值 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e)//表名删除的是链表的头 table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; }
与HashMap相对应的还有HashTable,HashMap和HashTable有什么区别
看一下HashTable源码:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
可以看到,HashTable和HashMap一样,都是实现Map来实现的
来看put方法:
/** * Maps the specified <code>key</code> to the specified * <code>value</code> in this hashtable. Neither the key nor the * value can be <code>null</code>. <p> * * The value can be retrieved by calling the <code>get</code> method * with a key that is equal to the original key. * * @param key the hashtable key * @param value the value * @return the previous value of the specified key in this hashtable, * or <code>null</code> if it did not have one * @exception NullPointerException if the key or value is * <code>null</code> * @see Object#equals(Object) * @see #get(Object) */ public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry tab[] = table; int hash = hash(key); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V old = e.value; e.value = value; return old; } } modCount++; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = hash(key); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. Entry<K,V> e = tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; return null; }
从上面可以看到,增加了synchronized关键字,所以是线程安全的,还有注释说到,其key和value不能为空
所以HashMap和HashTable的区别如下:
1.HashTable是线程安全的,HashMap不是线程安全的
2.HashMap可以存储key和value为null的值,HashTable不能
3.还有一个区别HashMap的Iterator是fail-fast机制的,所以HashMap在迭代过程中,如果有集合有被修改,那么将会抛出ConcurrentModificationException异常,
而Hashtable的enumerator迭代器不是fail-fast.
TreeMap
TreeMap使用红黑树来实现,是一种有序的数据结构,因为总是处于一种平衡的状态,所以没有调优因子。
TreeMap和HashMap的区别
HashMap是hash表来实现,TreeMap使用红黑树来实现
TreeMap是有序的,HashMap是无序的
怎么选择使用HashMap还是TreeMap了
如果插入和更新都比较频繁的话,那么保证元素的有序可以提高快速和频繁查找的性能。如果对于排序操作(例如产生一个报表合作者运行一个批处理程序)的要求不是很频繁的话,那么把数据以无序的方式存储,然后在需要排序的时候用Collections.sort(…)来进行排序,会比用有序的方式来存储可能会更加高效