先看看hashmap与hashtable中的数据结构
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;//存储冲突中链表下一个元素
final int hash;//依靠hash来索引map
从构造器看看两者不同
hashmap默认初始化容量为16;当使用非默认构造器时,其初始容量并非为所设置的容量initialCapacity,而是使用capacity,这里对初始容量做了个优化,保证初始容量为2的倍数,可以自己研究下原因。
还有在使用构造器初始化的时候最终hashmap都会调到init();
/**
* Initialization hook for subclasses. This method is called
* in all constructors and pseudo-constructors (clone, readObject)
* after HashMap has been initialized but before any entries have
* been inserted. (In the absence of this method, readObject would
* require explicit knowledge of subclasses.)
*/
void init() {
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
public HashMap(int initialCapacity, float loadFactor) {
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;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
hashtable默认初始化容量为11,并且需要调用Hashtable(int initialCapacity, float loadFactor);
public Hashtable() {
this(11, 0.75f);
}
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);
}
看完初始化我们看下起最主要的不同方法PUT;
hashmap中,支持key和value都为空的情况,使用的hash为重新计算后的hash值,使用计算位置的方式也不一样 h & (length-1);当hashmap中的容量超过临界值时是将容量扩大为原来的1倍2 * table.length;
hashtable(同步)中当key为空的时候就直接报空指针,hash使用的是类自身的hashcode方法,计算位置使用的(hash & 0x7FFFFFFF) % tab.length;当hashtable中的容量超过临界值时是将容量扩大为oldCapacity * 2 + 1;
还可以看下两者在重新分配空间时候的做法。
public V put(K key, V value) {
//处理key为空的特殊情况
if (key == null)
return putForNullKey(value);
//使用key的hashcode再hash
int hash = hash(key.hashCode());
//i为放在hashmap中的位置
int i = indexFor(hash, table.length);
//当相同位置有多个值时候,循环查找。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果key为一样则返回原来的旧值,把新值插入到原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++;
//把对应的新map添加到i位置中
addEntry(hash, key, value, i);
return null;
}
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//把每个冲突节点的旧链表放入Entry的next中
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
//如果map中内容超出threshold则将容量扩大1倍
resize(2 * table.length);
}
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
//构造新的map空间
Entry[] newTable = new Entry[newCapacity];
//将原有map中内容重新hash到新的位置
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
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) {
//释放原有table在内存中的位置
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);
}
}
}
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 = 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 the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);
count++;
return null;
}
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;
}
}
}
其他都有的方法基本类似
参考
http://blog.csdn.net/romans1981/archive/2005/03/07/313232.aspx
http://tieba.baidu.com/f?kz=890094718
分享到:
相关推荐
在Java编程语言中,`HashMap`和`HashTable`都是实现键值对存储的数据结构,但它们之间存在一些显著的区别,这些区别主要体现在线程安全性、性能、null值处理以及一些方法特性上。以下是对这两个类的详细分析: 1. ...
Java集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。
然而,HashMap与HashTable的主要区别在于线程安全性:HashTable是线程安全的,而HashMap则不是。 JDK1.8之后,HashMap进行了性能优化,引入了红黑树的数据结构。当链表的长度超过一个阈值(默认为8)时,HashMap会...
2、相比网站上发布过的hashtable之类的源码:。此HashMap寻址方法是拉链法.比开放寻址法对连续内存要求更低。哈希函数用的是java中String.hashCode()算法(经实际验证其碰撞率极低且相近的文本散列值相邻,存取的效率更...
`HashMap`允许存储键值对,并且支持使用`null`作为键或值,这与`Hashtable`有所不同,后者不允许键或值为`null`。`HashMap`通过散列表来存储数据,这种设计使得在大多数情况下,其插入、删除和查找操作的时间复杂度...
Java源码解析HashMap简介 HashMap是Java开发中最常用的集合之一,它基于hash表的实现,提供了map的所有可选操作,并且允许null值和一个null的key。HashMap的实现提供了常量级的性能,假设hash函数把元素们比较好的...
9. **HashMap与HashTable的区别**:HashTable是早期的线程安全版本,但它不允许null键和值,且性能较低,已经被ConcurrentHashMap所取代。 10. **面试常见问题**:面试中常问的问题包括HashMap的扩容机制、哈希冲突...
HashMap不是线程安全的,如果需要线程安全的Map,可以使用Hashtable。 LinkedHashMap与HashMap类似,但保持了插入顺序或访问顺序。TreeMap使用红黑树,保证了键的排序。 总的来说,理解这些集合的底层实现对于优化...
本文将详细探讨HashMap的底层原理,包括其数据结构、存取实现以及与Hashtable的区别。 1. HashMap的数据结构 数据结构是计算机存储、组织数据的方式,它决定了数据的操作效率。HashMap使用数组和链表的组合来实现,...
5. **HashMap与Hashtable的区别**:`HashMap`是非同步的,适合于单线程环境,而`Hashtable`是同步的,可以在多线程环境中安全使用。`HashMap`允许`null`键和值,而`Hashtable`不允许。 6. **TreeMap与TreeSet的特点...
### HashMap多线程解决方案 #### 一、引言 在多线程环境下,Java的`HashMap`类在处理并发操作时容易出现线程安全问题。本文档深入探讨了`HashMap`在多线程环境中可能遇到的安全问题,并提出了一系列可行的解决方案...
本文主要讲述了 Java 中的并发编程,包括 atomic 包的介绍、CAS 算法的原理、ABA 问题的解决方案,以及 collections 中的 HashMap、HashTable 和 ConcurrentHashMap 的源码分析。 Atomic 包的介绍 ----------------...
- HashMap和Hashtable的区别? - HashMap与HashSet的关系? - 如何解决哈希冲突? - 如何自定义键的哈希码生成方式? - 如何避免和处理HashMap中的循环链表? 通过深入学习和理解这些知识点,你将能够在面试中自信...
hashmap源码 Notes 原理 basic 1 数据结构中各种东西的数量很很重要!!!可以考虑在在数据结构中定义一下 2 Java hashtable的contains用来查找是否存在value,和containsValue类似 查找key使用containskey方法, 3 ...
在这个“hashtable C++无锁(std_atomic)&U32非0&不可扩大.rar”压缩包中,包含了一个C++实现的无锁哈希表,其关键特性是使用了std::atomic保证线程安全性,并且键值类型为U32(无符号32位整数),并且哈希表的大小不...
hashmap源码 Java 面试随着时间的改变而改变。在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象。因此在每次对 String ...
2、相比网站上发布过的hashtable之类的源码: 此HashMap寻址方法是拉链法.比开放寻址法对连续内存要求更低 哈希函数用的是java中String.hashCode()算法(经实际验证其碰撞率极低且相近的文本散列值相邻,存取的效率更高...
HashMap实现了Map接口,允许放入null元素,除该类未实现同步外,其余跟Hashtable大致相同,跟TreeMap不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,因此不同时间...
在给定的压缩包文件中,包含了一些关键的集合类源码,如`TreeMap`、`Hashtable`、`ArrayList`、`HashMap`、`LinkedList`、`List`、`Map`、`TreeSet`、`LinkedHashMap`和`Set`。这些类都是Java集合框架的重要组成部分...