HashMap的底层实现原理?
以jdk7为例进行说明。
HashMap map=new HashMap()
在实例化之后,底层创建了长度是16的一维数组Entry[] table
…执行过多次put
map.put(key1,value1):
首先,调用hashCode()计算key1哈希值,此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。如果此位置上的数据不为空,意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。
如果key1的哈希值和已经存在的某一个数据的哈希值相同(hash碰撞),继续比较:调用key1所在类的equals方法,如果true,实用value1替换value2
false 则添加成功
不断添加的过程中会遇到扩容问题:默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8的不同:
- new HashMap() 底层没有创建一个长度16的数组
- jdk8底层的数组是Node[],不是Entry[]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk7的底层结构只有:数组+链表。jdk8中的底层结构:数组+链表+红黑树
党数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前的数组长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储
HashMap的存储结构(jdk1.8前)
hashmap数组扩容后最消耗性能的点: 原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize
什么时候扩容?
当hashmap中的元素个数超过数组大小loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75
默认情况下,数组大小为16,那么当hashmap中元素个数超过160.75=12(代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为2*16=32,扩大一倍,然后重新计算每个元素在数组中的位置,而这时一个飞航消耗性能的操作。所以如果我们已经阈值hashmap中元素的个数,那么预设元素的个数能够有效地提高hashmap的性能。
HashMap的存储结构 JDK1.8
内部存储结构变成了 数组+链表+树的结合。当实例化一个hashmap时,系统会创建一个长度为initialCapacity的Node数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为“桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速查找bucket中的元素。
一个桶中:Node链/TreeNode对象。新元素作为链表last(尾插?),树的叶子。
扩容和树形化:
当hashmap中的其中一个链的对象个数达到8个,此时如果capacity没有达到64,那么hashmap会先扩容解决。如果已经达到64,那么这个链会变成树,结点类型由Node类型变为TreeNode类型。
如果当映射关系被移除后,下次resize方法时判断树的结点个数小于6个,也会把树再转为链表。
Properties
操作配置文件
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
//Properties常用来处理配置文件。key和value都是String类型
public static void main(String[] args) {
FileInputStream fis=null;
try {
Properties pros=new Properties();
fis=new FileInputStream("jdbc.properties");
pros.load(fis);//加载流对应文件
String name=pros.getProperty("name");
String password=pros.getProperty("password");
System.out.println("name="+name+",password="+password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis!=null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}