生活随笔
收集整理的这篇文章主要介绍了
HashMap HashTable HashSet区别剖析
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
HashMap、HashSet、HashTable之间的区别是Java程序员的一个常见面试题目,在此仅以此博客记录,并深入源代码进行分析:
在分析之前,先将其区别列于下面
1:HashSet底层采用的是HashMap进行实现的,但是没有key-value,只有HashMap的key set的视图,HashSet不容许重复的对象
2:Hashtable是基于Dictionary类的,而HashMap是基于Map接口的一个实现
3:Hashtable里默认的方法是同步的,而HashMap则是非同步的,因此Hashtable是多线程安全的
4:HashMap可以将空值作为一个表的条目的key或者value,HashMap中由于键不能重复,因此只有一条记录的Key可以是空值,而value可以有多个为空,但HashTable不允许null值(键与值均不行)
5:内存初始大小不同,HashTable初始大小是11,而HashMap初始大小是16
6:内存扩容时采取的方式也不同,Hashtable采用的是2*old+1,而HashMap是2*old。
7:哈希值的计算方法不同,Hashtable直接使用的是对象的hashCode,而HashMap则是在对象的hashCode的基础上还进行了一些变化
源代码分析:
对于区别1,看下面的源码
[java] view plaincopy
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; private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<E,Object>(); } 从上面的代码中得出的结论是HashSet的确是采用HashMap来实现的,而且每一个键都关键同一个Object类的对象,因此键所关联的值没有意义,真正有意义的是键。而HashMap里的键是不允许重复的,因此1也就很容易明白了。
对于区别2,继续看源代码如下
[java] view plaincopy
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { [java] view plaincopy
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable 区别3,找一个具有针对性的方法看看,这个方法就是put
[java] view plaincopy
public synchronized V put(K key, V value) { if (value == null) { throw new NullPointerException(); } Entry tab[] = table; int hash = key.hashCode(); 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(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length; } [java] view plaincopy
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); 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; } 区别4在上面的代码中,已经分析了,可以再细看一下
区别5内存初化大小不同,看看两者的源代码:
[java] view plaincopy
public Hashtable() { this(11, 0.75f); } [java] view plaincopy
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } 从上面的代码中,可以看出的是两者的默认大小是不同的,一个是11,一个是16
区别6内存的扩容方式,看一看源代码也是很清楚的,其实区别是不大的,看到网上一哥们写的,说两者有区别,其实真正深入源码,区别真不大,一个是2*oldCapacity+1, 一个是2*oldCapacity,你说大吗:)
[java] view plaincopy
protected void rehash() { int oldCapacity = table.length; Entry[] oldMap = table; int newCapacity = oldCapacity * 2 + 1; Entry[] newMap = new Entry[newCapacity]; modCount++; threshold = (int)(newCapacity * loadFactor); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newMap[index]; newMap[index] = e; } } } [java] view plaincopy
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); } 是吧,没什么区别吧
对于区别7的哈希值计算方法的不同,源码面前,同样是了无秘密
[java] view plaincopy
int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; [java] view plaincopy
int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); 上面的HashMap中可以看出关键在两个函数hash与indexFor
源码如下:
[java] view plaincopy
static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } [java] view plaincopy
static int indexFor(int h, int length) { return h & (length-1); }
转载于:https://www.cnblogs.com/dangzhenjiuhao/p/5416470.html
总结
以上是生活随笔为你收集整理的HashMap HashTable HashSet区别剖析的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。