项目中Map的使用非常频繁,对于map的知识点的学习回顾是非常有必要的
知识点:
-
HashMap 中的方法在默认情况下是非同步的,这一点和HashTabel对比就可以看出,HashTabel的方法是有synchronized关键词修饰的
-
HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null
@Test
public void test(){
Map<String, Object> map = new HashMap<>();
map.put(null, null);
System.out.println(map);
}
输出结果:{null=null}
往HashMap中存放键值对时,如果key相同,则最新加入的值会覆盖掉原有的值
@Test
public void test(){
Map<String, Object> map = new HashMap<>();
map.put(null, null);
map.put(null, "123");
System.out.println(map);
}
输出的结果为:{null=123}
ctrl+fn+F12查看文件结构
HashMap 实现 Iterator,支持 fail-fast
fail-fast是集合世界中错误检测机制,通常出现在集合元素的遍历过程中, java.util包下所有的类都是fail-fast,而concurrent包中的集合都是fail-safe
HashMap如何遍历
方法一: 测试demo
@Test
public void test(){
Map<String, Object> map = new HashMap<>();
map.put(null, null);
map.put("name", "123");
map.put("address", "中国");
map.put("age", "30");
for(Map.Entry<String, Object> entry : map.entrySet()){
System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}
}
输出结果:
key = null, value = null
key = address, value = 中国
key = name, value = 123
key = age, value = 30
For-Each循环是Java5新引入的,所以只能在Java5以上的版本中使用。如果你遍历的map是null的话,For-Each循环会抛出NullPointerException异常,所以在遍历之前你应该判断是否为空引用
方法二:
- 仅获取key
@Test
public void getKey(){
Map<String, Object> map = new HashMap<>();
map.put(null, null);
map.put("name", "123");
map.put("address", "中国");
map.put("age", "30");
for (String key : map.keySet()) {
System.out.println("Key = " + key);
}
}
// lambda表达式来写的话就一行代码
map.forEach((k,v) -> System.out.println(k));
结果:
Key = null
Key = address
Key = name
Key = age
- 仅获取value
@Test
public void getvalue(){
Map<String, Object> map = new HashMap<>();
map.put(null, null);
map.put("name", "123");
map.put("address", "中国");
map.put("age", "30");
for (Object value : map.values()) {
System.out.println("Value = " + value);
}
}
map.forEach((k,v) -> System.out.println(v));
输出结果:
Value = null
Value = 中国
Value = 123
Value = 30
特点
HashMap中的方法都是异步处理,属于非线程安全
问题
1 为什么HashMap初始容量是2<<4 ?或HashMap的初始容量是多少
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
这个上边注释的意思是,默认初始容量-必须是2的幂
作用:
1.提醒你这个容量就是2的幂,扩容方式也是2的幂。
2.二的幂是使得Key Hash算法后的值尽可能均匀的分布在Map对应的数组位置的合理值
size记录hashMap中KV对的个数
/**
* The number of key-value mappings contained in this map.
*/
transient int size;
阈值:
// TREEIFY_THRESHOLD表示树形阈值
static final int TREEIFY_THRESHOLD = 8;
在HashMap中存储数据超过8的时候(阈值) 就会由链表转为红黑树(类似于二分查找)
扩容机制
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
HashMap中对于数据的保存个数的扩充是按照倍数来进行的,如果达到了16*0.75个数的时候回进行第一次扩充,而后扩充一倍变为32
hash(Object key)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
HashMap的原理:
JDK1.8之前
JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 -判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突
JDK1.8
JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
HashMap中哈希冲突严重时会影响HashMap性能,该如何做
首先要知道什么是哈希冲突,在整个Hash存储过程中,必须要明确两个实际问题,
hashcode()和equal()
Map.Entry和Node<K,V>
参考文章:https://www.hollischuang.com/archives/2091
Java8中遍历Map的常用四种方式
HashTable
HashTable(线程安全)
Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换
待时间充足再更新.....
评论区