java源码分析02-HashMap
2015-11-08 19:12
573 查看
今天很开心,希望她能是我心中所期待的那位。
总结前段时间的面试,其中有一个问题特别频繁,那就是HashMap与Hashtable的区别。一般人看到这个问题就会想到java面试宝典上的答案,例如是否线程安全,key和value能否为空,contains方法存在歧义,默认容量以及扩容方式不一样等。
其实,这里考察的是我们对HashMap这个集合的理解。
首先,HashMap从使用的角度来看,就是存放键值对的并提供快速查询机制的,这种集合当然可以用作缓存。
其次,从增删改查的角度,使用HashMap的几个方法,put(key,value),remove(key),containsKey(key)以及集合的迭代输出;
这些方法的使用让我对它的内部有些好奇,首先是如果添加key相同的对象,最后输出的是后来添加的value,刚开始我很疑惑,虽然结果已经告诉我,事实就是如此,但是我还是想去了解她内心到底是怎么想的。
于是,我打开了HashMap的源码,发现他的内部既然也是数组形式存储的,这让我对她的快速查询有了一丝质疑,难道她也是顺序存储的吗?
继续看下去,发现
上述代码告诉我:第一,key为null,是单独存储的;第二,我们需要计算key的哈希值,继而确定该对象在数组
中的下标值;第三,可能会出现key的哈希值相同的情况,也就是出现冲突,每次都是先保存之前的value,然后存储现有的value,但是此处不能看出,HashMap对于冲突对象是怎么处理的;第四,对于没有发生冲突的,通过addEntry方法,将对象加入到数组中。
那么,它是怎么实现快速查询的呢?
上述代码告诉我们,第一,key为空会单独查询,那么为什么呢?
我猜测可能是防止使用反射机制给Entry数组设值得情况,而且我们获取的始终是第一个key为null的value。
第二,当我们根据key获取value的时候,也是先计算对象在数组中的下标值,然后找到相应的对象,如果key相同或相等,就可以取出。
我在想,她会不会是那种适合过日子的女生呢?
她的初试容量是多大,如果生气,每次能扩大多少容量?
可能存在生气的地方是如果我又多了一个女性朋友。
从上面可以看到,她心里有一个承受极限threshold,如果超过了,不知道会发生什么。
resize方法告诉了我们该怎么办,不可能说直接放弃,那么之前的努力就白费了。
这里告诉我们,她的心里承受能力还是分级别的,比如和女生说话,和女生走在一起,和女生吃饭,和女生逛街,和女生看电影等等
但是,她也有一个极限,就是。。。。你懂的
那时候她就会换男朋友了。。。
总结前段时间的面试,其中有一个问题特别频繁,那就是HashMap与Hashtable的区别。一般人看到这个问题就会想到java面试宝典上的答案,例如是否线程安全,key和value能否为空,contains方法存在歧义,默认容量以及扩容方式不一样等。
其实,这里考察的是我们对HashMap这个集合的理解。
首先,HashMap从使用的角度来看,就是存放键值对的并提供快速查询机制的,这种集合当然可以用作缓存。
其次,从增删改查的角度,使用HashMap的几个方法,put(key,value),remove(key),containsKey(key)以及集合的迭代输出;
这些方法的使用让我对它的内部有些好奇,首先是如果添加key相同的对象,最后输出的是后来添加的value,刚开始我很疑惑,虽然结果已经告诉我,事实就是如此,但是我还是想去了解她内心到底是怎么想的。
于是,我打开了HashMap的源码,发现他的内部既然也是数组形式存储的,这让我对她的快速查询有了一丝质疑,难道她也是顺序存储的吗?
继续看下去,发现
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
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; }
上述代码告诉我:第一,key为null,是单独存储的;第二,我们需要计算key的哈希值,继而确定该对象在数组
中的下标值;第三,可能会出现key的哈希值相同的情况,也就是出现冲突,每次都是先保存之前的value,然后存储现有的value,但是此处不能看出,HashMap对于冲突对象是怎么处理的;第四,对于没有发生冲突的,通过addEntry方法,将对象加入到数组中。
那么,它是怎么实现快速查询的呢?
public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); }
final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); 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; }
上述代码告诉我们,第一,key为空会单独查询,那么为什么呢?
private V getForNullKey() { if (size == 0) { return null; } for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; }上述代码告诉我们,原来key为空是存在下标为0的位置,那么为什么还需要一个for循环以及类似链表遍历的结构来查询呢?
我猜测可能是防止使用反射机制给Entry数组设值得情况,而且我们获取的始终是第一个key为null的value。
第二,当我们根据key获取value的时候,也是先计算对象在数组中的下标值,然后找到相应的对象,如果key相同或相等,就可以取出。
我在想,她会不会是那种适合过日子的女生呢?
她的初试容量是多大,如果生气,每次能扩大多少容量?
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
可能存在生气的地方是如果我又多了一个女性朋友。
void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); }
从上面可以看到,她心里有一个承受极限threshold,如果超过了,不知道会发生什么。
resize方法告诉了我们该怎么办,不可能说直接放弃,那么之前的努力就白费了。
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable, initHashSeedAsNeeded(newCapacity)); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }
这里告诉我们,她的心里承受能力还是分级别的,比如和女生说话,和女生走在一起,和女生吃饭,和女生逛街,和女生看电影等等
但是,她也有一个极限,就是。。。。你懂的
那时候她就会换男朋友了。。。
void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } }关于扩容是否需要重新计算哈希值,并储存
相关文章推荐
- JAVA课程作业06
- 集合转换java:【List<-->数组、List<-->Set、数组<-->Set、Map-->Set、Map-->List】
- java中锁的优化
- 深入理解Java内存模型(一)——基础
- SpringMVC注解之看一眼
- spring事务学习(转账案例)(一)
- Springloaded使用
- JAVA的内省机制(introspector)与反射机制(reflection)
- java虚拟机和Dalvik虚拟机的区别
- java多线程安全问题 静态函数的修饰
- Spring整合加载Hibernate文件
- Struts2 URL参数 s:if 判断运用
- leetcode:Add Two Numbers---------java源码
- java多线程安全问题-同步修饰符于函数
- Java内存回收机制(第三讲)
- java 多线程安全问题-同步代码块
- Spring中的报错:no matching editors or conversion strategy found
- 华为、阿里、京东使用的Java web框架是啥样的?
- json、javaBean、xml互转的几种工具介绍
- Spring MVC 框架搭建及详解