无冥冥之志者,无昭昭之明;无惛惛之事者,无赫赫之功。
解释:没有专心致志地刻苦学习,就没有融会贯通的智慧;没有埋头执着的工作,就不会取得显著的成就。
HashMap
HashMap是Java中相当常见的数据结构。
1. 用来干什么?
它用来存储键值对。存储类型必须是引用类型,不能是基本类型。
2. 如何使用?有什么注意点?
HashMap<String> map = new HashMap<>();
map.put("key","value");
如上,HashMap的使用非常简单。
注意点:
- 存储类型必须是引用类型,不能是基本类型。
- HashMap的key和value都可以为 null。
- HashMap不是线程安全的
3. 实现原理是什么?学习原理过程中有什么收获?
HashMap的实现使用数据加链表的组合。HashMap内部有一个Entry数据结构类,一个Entry对象可以是一个链表的节点。同时HashMap内部有一个Entry的数组,数组的大小默认是16,也可以在创建HashMap对象的时候指定。
HashMap<String> map = new HashMap<>(64);//指定大小64
当需要存储一个value时,首先通过计算value的hashcode值,确定value在HashMap数组中的索引。如果当前索引位置以存放数据,即发生hash冲突,这时候HashMap会使用链地址法解决hash冲突。链地址法解决hash冲突的做法是:将需要存放的value连接在发生冲突的索引位置的链表上。
4. 注意点
HashMap指定数组大小为什么是2n ?通常指定HashMap大小的时候,这个大小需要是2n。即便指定的值不是2n,系统也会自动的指定。比如,指定这个数组大小是15,系统最终会指定大小为16。原因:
提高性能。其实主要是为了方便计算索引值。存储值Value的时候,索引值的计算:首先计算value的hashCode,然后求hashCode与数组长度的模,及:index = hashCode % length。 而任意一个值x,x与2n-1进行&运算和x与2n-1进行%运算结果是一样的,即:x & 2n-1 == x % 2n-1。以前学《数字逻辑》知道,& 运算 性能比 % 运算高。因此这里规定HashMap指定数字大小的时候尽量使用2n,是为了提高性能。
减少hash冲突。指定数组大小是:2n,那么计算索引的时候公式是:index = hashCode & 2n-1。2n-1肯定是奇数,这就确保了在&运算过程中,结果的最后一位可能是奇数也可能是偶数。显然这可以达到减少hash冲突的效果。
- 扩容问题
前面讲到HashMap中有一个数组存放数据。由于存放数据的时候,存放位置是通过需要存放数据的hashCode来决定的,所以这个数组有可能出现没有存满的问题。事实上,HashMap的这个数组存放量由一个加载因子控制,默认加载因子的大小是0.75。比如设定HashMap的大小是16,那么:16 X 0.75 = 12。当HashMap中数组中存放量大于等于12的时候,就会触发扩容机制。
- 线程不安全
HashMap不是线程安全的,多线程场景下,使用HashMap可能会发生意想不到的事情,比如发生链表成环。
- Java 1.8
从Java1.8开始,为了解决链表长度大,搜索时间太长过长问题,当链表长度大于8的时候,将链表转成红黑树,优化性能。