写在前面:
视频是什么东西,有看文档精彩吗?
视频是什么东西,有看文档速度快吗?
视频是什么东西,有看文档效率高吗?
诸小亮:下面我们学习——Map
张小飞:这个我知道,Map——双列集合,也称映射、字典,没错吧
诸小亮:不错,那你可知道它存储什么样的数据?
张小飞:Map是用来存储**键值(key,value)**数据的
诸小亮:嗯嗯, 说的不错,Map 可以说是开发中最常使用的集合了,主要特点:
1. 一次存储一对儿数据,称为键值对
2. 能够保证key的唯一性
常用方法
诸小亮:既然你已经提前看过了,可否说一说?
张小飞:嗯,我试试吧
张小飞:首先 Map 其实是一个接口,更 Collection 类似,里面定义了很多共性的方法
诸小亮:那,都有哪些方法呢?
张小飞:这可不少,另外因为 Map 是个接口,所以用它的具体实现类 HashMap 演示
put
张小飞:put(key,value):添加元素,每次添加一对儿数据
public static void main(String[] args) throws Exception {
//创建map对象,一般都声明泛型,限制存储的数据类型
Map<String, String> map = new HashMap<>();
//添加元素
map.put("yase","zhenji");
System.out.println(map);
}
结果:
诸小亮:我记得 put 方法有返回值啊
张小飞:是的,返回的是指定key对应的值,比如:
public static void main(String[] args) throws Exception {
Map<String, String> map = new HashMap<>();
map.put("yase","zhenji");
//因为map的key不能重复,所以新值覆盖老值,但是会返回老值
String str = map.put("yase", "xishi");
System.out.println(str);
}
结果:
putAll
putAll(Map<k,v> map):把指定参数中的数据,都添加到当前 map 中
演示:省略
get
get(key):根据key获取value
containsKey
containsKey(key):是否包含指定的key,true:是,false:否
containsValue
containsValue(value):是否包含指定的value,true:是,false:否
isEmpty
isEmpty():判断是否空的,true:是,false:否
remove
remove(key):删除指定的key,返回对应的value
结果:
clear
clear():清空整个map
结果:
keySet()
keySet():把map中的所有key放到一个Set集合中返回,常用来遍历整个map
public static void main(String[] args) throws Exception {
Map<String, String> map = new HashMap<>();
map.put("yase","zhenji");
map.put("lvbu","diaochan");
map.put("lixin","xishi");
//遍历整个map
Set<String> keys = map.keySet();
for(String key : keys) {
System.out.println(map.get(key));
}
}
结果:
entrySet()
entrySet():返回包含映射关系的 Set 集合,也是常用的一种遍历方式
public static void main(String[] args) throws Exception {
Map<String, String> map = new HashMap<>();
map.put("yase","zhenji");
map.put("lvbu","diaochan");
map.put("lixin","xishi");
// entrySet 返回Set集合,存储的是Map.Entry类型的对象
// 一个Map.Entry就是map中的一对儿key,value
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String,String> en : entries){
//一个 Map.Entry 中只包含一对key,value
System.out.println(en.getKey()+"----"+en.getValue());
}
}
结果:
values
values():把map中的所有value放到一个集合中返回
结果:
问题:为什么 values() 方法返回的不是 Set 集合?
答案:因为 map 中的 value 可能重复,而 Set 会自动去重
HashMap
诸小亮:我看你上面用的都是 HashMap 这个子类,对这个的挺多的啊
张小飞:还好吧,因为传说工作中 HashMap 使用的最频繁,所以一直用这个
诸小亮:嗯,你说的不错,工作中确实它使用的最多,那你能说说它的底层结构吗?
数据结构
张小飞:当然可以,数据结构是:可变数组 + 链表,其实 HashSet 内部的就是 HashMap
诸小亮:嗯,功课做的不错,继续说
张小飞:其数组,默认长16,当元素个数超过 数组大小 loadFactor 时,数组会扩容一倍
诸小亮:这个 _loadFactor 是?
张小飞:_loadFactor 是加载因子,默认值=0.75 ,表示当元素数量到 12 的时,数组扩容
诸小亮:那么,如何避免数组频繁的扩容呢?
张小飞:创建 HashMap 对象时,可以指定初始用量的大小,也就是可变数组的长度
诸小亮:然后呢?
张小飞: 一般建议,如果能提前确定需要存储的元素数量,在创建 HashMap 对象时候就要指定大小,避免
HashMap 频繁扩容,比如:
假如需要存储1200个元素,那么大小应该是:1200 / 0.75 = 1600;
因为是必须是2的N次方,最终值应该是比1600大且最接近的2的N次方的整数:2048
注意:其实在执行 new HashMap<>(1600); 时,其内部自动计算出2048,并设置为初始值
诸小亮:嗯,很不错,超乎我的意料
诸小亮:不过,JDK8 之后,HashMap底层做了优化啊
张小飞:是的,JDK8 之后,HashMap 的底层数据结构为:数组+链表+红黑树
- 当数组的长度大于 64 且链表长度大于 8 时,链表会转换成红黑树结构
诸小亮:为什么在链表长度大于 8 的时候转换?
张小飞:有两点原因
1. 根据默认的算法,HashMap中链表的长度>8的概率是非常低的,小于千万分之一
设置为 8 的原因是:不希望把链表转换为红黑树
2. 链表长度大于 8 后平均查询效率没有红黑树高
无序
诸小亮:什么是无序?
张小飞:就是,在 put 数据时,key的存放无法保证顺序,比如:
public static void main(String[] args) throws Exception {
HashMap<String, String> map = new HashMap<>(1200);
map.put("daji","女仆咖啡");
map.put("yase","心灵战士");
for(Map.Entry<String,String> en : map.entrySet()){
//一个 Map.Entry 中只包含一对key,value
System.out.println(en.getKey()+"----"+en.getValue());
}
}
结果:,取出的顺序,跟存放的顺序不同
空key 和 空value
张小飞:Map中,允许 null 作为key,null 作为 value,比如:
public static void main(String[] args) throws Exception {
Map<String, String> map = new HashMap<>();
map.put(null,"xishi");//key可以是null
System.out.println(map.get(null));
map.put("yase",null);//value也可以是null
System.out.println(map.get("yase"));
}
结果:
LinkedHashMap
诸小亮:说一说 LinkedHashMap 吧
张小飞:LinkedHashMap——是 HashMap 的子类,它最大的特点
- 可以保证 key 存放时候的顺序
public static void main(String[] args) throws Exception {
Map<String, String> map = new LinkedHashMap<>();
map.put("yase","zhenji");
map.put("lvbu","diaochan");
map.put("lixin","xishi");
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String,String> en : entries){
System.out.println(en.getKey()+"----"+en.getValue());
}
}
结果:,按照存放的顺序取出key,value
诸小亮:为什么它可以保证顺序呢?
张小飞:原理跟 LinkedHashSet 一样
TreeMap
诸小亮:我记得,还有一个 TreeMap 吧
张小飞:是的,TreeMap——是 Map 的具体子类
数据结构:
- 二叉树结构,其实 TreeSet 内部的就是 TreeMap
- 内部自动对 key 进行排序,比如:
public static void main(String[] args) throws Exception {
Map<String, String> map = new TreeMap<>();//注意,这里是无参数的构造方法
//这时候存储的key,必须实现Comparable接口
map.put("yase","zhenji");
map.put("lvbu","diaochan");
map.put("lixin","xishi");
map.put("anqila","xiaohongnv");
System.out.println(map);
}
结果:
张小飞:另外,TreeMap还有一个构造方法:TreeMap(Comparator<? super K> comparator);
- 使用它创建对象时,需要提供一个 Comparator 比较器
HashTable
张小飞:还有一个HashTable——也是map的具体子类
诸小亮:它更 HashMap 有什么区别?
张小飞:HashMap就是为替代它而生,主要区别:
1. Hashtable 是不允许键或值为 null ,HashMap 的键值都可以为 null
2. HashTable 都是同步方法
3. HashMap 的初始容量为:16,Hashtable 初始容量为:11
4. HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1