跟我一起阅读Java源代码之HashMap(三)
上一节我们讲到了如何用散列和链表实现HashMap,其中有一个疑问今天已经有些答案了,为什么要用链表而不是数组
链表的作用有如下两点好处
1. remove操作时效率高,只维护指针的变化即可,无需进行移位操作
2. 重新散列时,原来散落在同一个槽中的元素可能会被散落在不同的地方,对于数组需要进行移位操作,而链表只需维护指针
今天研究下数组长度不够时的处理办法
table为散列数组
1. 首先定义一个不可修改的静态变量存储table的初始大小 DEFAULT_INITIAL_CAPACITY
2. 定义一个全局变量存储table的实际元素长度,size
3. 定义一个全局变量存储临界点,即元素的size>=threshold这个临界点时,扩大table的容量
4. 因为index是根据hash和table的长度计算得到的,所以还需要重新对所有元素进行散列
package sourcecoderead.collection.map; public class EntryHashMap<K, V> { /** 初始容量 */
static final int DEFAULT_INITIAL_CAPACITY = 16; static final float DEFAULT_LOAD_FACTOR = 0.75f; /** 下次扩容的临界值 */
int threshold; transient int size; final float loadFactor; transient Entry[] table; public EntryHashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
} public V put(K key, V value) {
// 计算出新的hash
int hash = hash(key.hashCode());
// 计算出数组小标i
int i = indexFor(hash, table.length);
// 遍历table[i],如果table[i]没有与新加入的key相等的,则新加入
// 一个value到table[i]中的entry,否则将新的value覆盖旧的value并返回旧的value
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;
return oldValue;
}
}
addEntry(hash, key, value, i);
return null;
} public V get(K key) {
// 计算出新的hash
int hash = hash(key.hashCode());
// 计算出数组小标i
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))) {
return e.value;
}
}
return null;
} private void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K, V> e = table[bucketIndex];
// 将新的元素插入链表前端
table[bucketIndex] = new Entry<>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
} void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
Entry[] newTable = new Entry[newCapacity];
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) {
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);
}
}
} /**
* 通过hash code 和table的length得到对应的数组下标
*
* @param h
* @param length
* @return
*/
static int indexFor(int h, int length) {
return h & (length - 1);
} /**
* 通过一定算法计算出新的hash值
*
* @param h
* @return
*/
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
} public static void main(String[] args) {
EntryHashMap<String, String> hashMap = new EntryHashMap<String, String>();
hashMap.put("key", "value");
System.out.println(hashMap.get("key"));
}
}
跟我一起阅读Java源代码之HashMap(三)的更多相关文章
- 跟我一起阅读Java源代码之HashMap(一)
最近闲的很,想和大家一起学习并讨论下Java的一些源代码以及其实现的数据结构, 不是什么高水平的东西,有兴趣的随便看看 1. 为什么要用Map,以HashMap为例 很多时候我们有这样的需求,我们需要 ...
- 跟我一起阅读Java源代码之HashMap(二)
上一节中实现的SimpleHashMap,没有解决冲突的问题,这一节我们继续深入 由于table的大小是有限的,而key的集合范围是无限大的,所以寄希望于hashcode散落,肯定会出现多个key散落 ...
- 源码阅读 - java.util.concurrent (三)ConcurrentHashMap
在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:ConcurrentMap.本篇文章主要关注ConcurrentMap接口以及它的Hash版本的实现Concu ...
- 使用eclipse阅读java源码
很多时候想要阅读java源代码(无论是jdk自带的,还在第三方的),但是我们使用的jar包都是编译好的class包,无法在eclipse中直接打开,此时需要下载源码包. 本文以查看HttpClient ...
- Java源代码之LinkedHashMap
Java源代码之LinkedHashMap 转载请注明出处:http://blog.csdn.net/itismelzp/article/details/50554412 一.LinkedHashMa ...
- 如何在Eclipse下查看JDK源代码以及java源代码阅读方法(转载)
不会看JDK源代码,相当于没学过Java. 网上不容易找到一篇帮助我解决了如何在Eclipse下查看JDK源代码 的文章. 核心提示:在Eclipse中查看JDK类库的源代码!!! 设置: 1.点 w ...
- java面试之Hashmap
在java面试中hashMap应该说一个必考的题目,而且HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接 ...
- JDK1.7.0_45源码阅读<java.lang.Boolean>
本文适合的人群 其实感觉写这个标题的内容没有必要,只要你觉得对你有帮助那么就适合你,对你没帮助那么就不适合你.毕竟我不是专业作者,但咱会尽力的.其实最重要的一点是我不希望浪费您宝贵时间. 简要把内容在 ...
- 全中国的省市县镇乡村数据获取以及展示java源代码
第一步.准备工作(数据源+工具): 数据源(截止目前最全面权威的官方数据):http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/ 爬取数据的工具 ...
随机推荐
- 12.Reflect
Reflect Reflect 概述 Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API.Reflect对象的设计目的有这样几个. (1) 将Object对象的一些明 ...
- [转] Hadoop管理员的十个最佳实践
前言 接触Hadoop有两年的时间了,期间遇到很多的问题,既有经典的NameNode和JobTracker内存溢出故障,也有HDFS存储小文件问题,既有任务调度问题,也有MapReduce性能问题.遇 ...
- CodeBlocks "no such file or directory" 错误解决方案(创建类找不到头文件)
在CodeBlocks下,有时候需要自己定义类,当然就要添加相应的头文件,但添加进去的头文件明明包含在项目中了, 但编译时还是会报错:no such file or directory;这是为什么呢? ...
- Android全屏的两种方法
在开发中我们经常需要把我们的应用设置为全屏,这里我所知道的有俩中方法,一中是在代码中设置,另一种方法是在配置文件里改! 一.在代码中设置: [java] view plain copy package ...
- 手把手教你写一个java的orm(三)
使用反射解析class 上一篇我们完成了class到表映射关系的建立,但是这个并不能被代码正确处理,我们还需要让程序能够正确的识别这些映射关系. 这一篇主要讲的是建立一个从class到表的模型,使我们 ...
- mysql 中 max_allowed_packet 查询和修改
mysql 会根据配置文件限制 server 接收的数据包的大小. 有时候大的插入和更新会被 max_allowed_packet 参数限制,报如下错误: Packet > ). You can ...
- spring 中 InitializingBean 接口使用理解
前言:这两天在看 spring 与 quart 的集成,所以了解到 spring 是如何初始化 org.springframework.scheduling.quartz.SchedulerFacto ...
- python学习之老男孩python全栈第九期_数据库day003 -- 作业
数据库: class: course: student: teacher: score: /* Navicat Premium Data Transfer Source Server : local ...
- opencv3.2.0形态学滤波之形态学梯度、顶帽、黑帽
/*一.形态学梯度 (1)含义:是膨胀图和腐蚀图之差 (2)数学表达式:dst=morph-grad(src,element) =dilate(src,element) - erode(src,ele ...
- 在RecyclerView列表滚动的时候显示或者隐藏Toolbar
先看一下效果: 本文将讲解如何实现类似于Google+应用中,当列表滚动的时候,ToolBar(以及悬浮操作按钮)的显示与隐藏(向下滚动隐藏,向上滚动显示),这种效果在Material Design ...