1、 散列表要解决的一个问题就是散列值的冲突问题,通常是两种方法:链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。java.util.HashMap采用的链表法的方式,链表是单向链表,因此在删除过程中要自己维持prev节点,我想不采用双向链表是从节省空间考虑。一个典型的查找过程:
- 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;
- }
HashMap采用链表法而不是开放地址法,猜想可能的原因是从实用角度出发,对空间和时间效率做出的折中选择。采用开放地址法,无论是线性探测或者二次探测都可能造成群集现象,而双重散列会要求散列表的装填程度比较低的情况下会有比较好的查找效率,容易造成空间的浪费。
2、 什么是负载因子?负载因子a定义为
a=散列表的实际元素数目(n)/ 散列表的容量(m)
负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。
回到HashMap的实现,HashMap中的loadFactor其实定义的就是该map对象允许的最大的负载因子,如果超过这个系数将重新resize。这个是通过threshold字段来判断,看threshold的计算:
- threshold = (int)(capacity * loadFactor);
结合上面的负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。注意到的一点是resize的规模是现有capacity的两倍:
- if (size++ >= threshold)
- resize(2 * table.length);
3、可能你也注意到了,java.util.HashMap对key的hash值多做了一步处理,而不是直接使用hashCode:
- static int hash(int h) {
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
这个处理的原因在于HashMap的容量总是采用2的p次幂,而取index(槽位)的方法是
- static int indexFor(int h, int length) {
- return h & (length-1);
- }
这一运算等价于对length取模,也就是
h % 2^p
返回的将是h的p个最低位组成的数字,我们假设hash输入是符合简单一致散列,然而这一假设并不能推论出hash的p个最低位也会符合简单一致散列,也许h的这p个最低位相同的几率很大,那么冲突的几率就非常大了。优秀的散列函数应该需要考虑所有的位。
因此为了防止这些“坏”的散列函数造成效率的降低,HashMap预先对hash值做了处理以考虑到所有的位,根据注释也可以知道。这个处理我看不懂,留待高人解释,也许来自于某本算法书也不一定。
4、 我们知道java.util.HashMap不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略(速错),这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount,
- Iterator() {
- expectedModCount = modCount;
- if (size > 0) { // advance to first entry
- Entry[] t = table;
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- }
在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了map
- final Entry<K,V> nextEntry() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
注意到modCount声明为volatile,保证线程之间修改的可见性。
相关推荐
例如,`java.util.Collection`接口新增了`default void forEach(Consumer<? super E> action)`方法。 6. **Optional 类**:一个容器对象,可能包含或不包含非null值。设计目的是减少空指针异常的发生,提高代码的...
6. **类型接口**:Java 8引入了泛型的类型接口,如`Supplier<T>`、`Consumer<T>`、`Function,R>`等,这些都是函数式接口,为lambda表达式提供基础。 7. **Optional类**:`java.util.Optional`类是一个容器对象,...
标题中的"apr-util-1.5.4.tar.gz"是一个开源软件库的归档文件,它属于Apache Portable Runtime (APR)项目的一部分。APR是一个为各种操作系统提供统一API的库,主要用于处理底层系统功能,如文件I/O、网络通信、进程...
`java-util-1.9.1.jar` 是一个通用的Java工具库,包含了各种实用的函数和类,帮助开发者更高效地编写代码。这个库可能包含了一些帮助处理日期时间、字符串、集合、网络请求等常见任务的工具类。在处理CORS时,虽然它...
Java Development Kit(简称JDK)是Oracle公司发布的用于开发和运行Java应用程序的工具包,它包含Java编译器、Java虚拟机(JVM)以及Java类库等组件。本篇将详细探讨 JDK 8u201 版本在Linux环境下的安装与使用。 ...
5. **日期与时间API**:`java.time`包替代了过时的`java.util.Date`和`java.util.Calendar`,提供了更直观、易用且功能丰富的日期和时间API,如`LocalDate`, `LocalTime`, `LocalDateTime`, `ZonedDateTime`等。...
4. **Date和Time API的改进**:提供了新的`java.time`包,替代了旧的`java.util.Date`和`java.util.Calendar`,提供了更好的日期和时间操作。 5. **Optional类**:用于处理可能为空的对象,防止空指针异常。 **在...
- Date/Time API的增强:引入了新的java.time包,替代了过时的java.util.Date和java.util.Calendar,提供了更强大且易用的日期和时间处理功能。 为了在系统中使用这个JDK,还需要配置环境变量。在.bashrc或.bash_...
- **Date和Time API的改进**:移除了旧的`java.util.Date`和`java.util.Calendar`,引入了新的`java.time`包,提供更强大、更易用的时间日期处理功能。 - **方法引用来替代匿名内部类**:可以引用一个方法的签名,...
5. **日期与时间API**:JDK 8用`java.time`包替代了过时的`java.util.Date`和`java.util.Calendar`,提供了`LocalDate`,`LocalTime`,`LocalDateTime`,`ZonedDateTime`等更易用的类,以及`Duration`和`Period`用于...
HBase操作类------->HBaseUtil.java 短信操作类-------->Sms.java 联系人操作类------>Contact.java 文件操作类-------->MyFile.java 上面的Action都配置到Struts.xml中。 Client端介绍: 封装了三个实体bean ...
- **Date和Time API**:全新的`java.time`包,替代了旧的`java.util.Date`和`java.util.Calendar`,提供了更强大和易用的时间日期处理功能。 - **默认方法**:允许在接口中定义默认实现,增强了接口的功能性。 - **...
在日期和时间API上,Java 8用`java.time`包替换了过时的`java.util.Date`和`java.util.Calendar`,提供了更直观、更易用的API,如`LocalDate`、`LocalTime`和`LocalDateTime`,以及`Duration`和`Period`等。...
5. **Date和Time API的改进**:Java 8提供了新的日期和时间API,以替换过时的java.util.Date和java.util.Calendar,提高了日期和时间处理的易用性和灵活性。 6. **新的 Nashorn JavaScript引擎**:允许在Java应用中...
赠送jar包:jetty-util-9.4.43.v20210629.jar; 赠送原API文档:jetty-util-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-util-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-util-9.4.43.v...
赠送jar包:jetty-util-ajax-9.3.19.v20170502.jar; 赠送原API文档:jetty-util-ajax-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-util-ajax-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-...
- **Date and Time API (JSR 310)**:提供了全新的日期时间API,比旧的`java.util.Date`和`java.util.Calendar`更强大,更易于使用。 - **接口默认方法**:接口中可以定义默认方法,为接口添加实现,而无需创建新的...
asm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util-3.2.jarasm-util...
4. **Date and Time API (JSR 310)**: 提供了新的日期和时间类,替代了不完善的`java.util.Date`和`java.util.Calendar`。 5. **默认方法**: 接口中可以定义带实现的方法,增强了接口的灵活性。 6. **Optional 类**:...