项目中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}
代码语言:javascript复制往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
代码语言:javascript复制 @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的初始容量是多少
代码语言:javascript复制 /**
* 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对的个数
代码语言:javascript复制 /**
* The number of key-value mappings contained in this map.
*/
transient int size;
阈值:
代码语言:javascript复制 // TREEIFY_THRESHOLD表示树形阈值
static final int TREEIFY_THRESHOLD = 8;
在HashMap中存储数据超过8的时候(阈值) 就会由链表转为红黑树(类似于二分查找)
扩容机制
代码语言:javascript复制 /**
* 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)
代码语言:javascript复制 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的常用四种方式
https://mp.weixin.qq.com/s?__biz=MzUyMzcyNjY4Mw==&mid=2247484703&idx=2&sn=1e5aa63f5806f8ebac6f066368b64d09&chksm=fa397f2bcd4ef63df718055f61fc506e0e47a847dbe6b21021815039267522209402abb02c79&scene=21#wechat_redirect
HashTable
HashTable(线程安全) Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换
待时间充足再更新.....