专业的JAVA编程教程与资源

网站首页 > java教程 正文

张小飞的Java之路——第三十七章——Map

temp10 2024-09-22 09:58:27 java教程 8 ℃ 0 评论

写在前面:

视频是什么东西,有看文档精彩吗?

张小飞的Java之路——第三十七章——Map

视频是什么东西,有看文档速度快吗?

视频是什么东西,有看文档效率高吗?



诸小亮:下面我们学习——Map

张小飞:这个我知道,Map——双列集合,也称映射、字典,没错吧

诸小亮:不错,那你可知道它存储什么样的数据?

张小飞:Map是用来存储键值(key,value)数据的


诸小亮:嗯嗯, 说的不错,Map 可以说是开发中最常使用的集合了,主要特点:

1. 一次存储一对儿数据,称为键值对
2. 能够保证key的唯一性

1. 常用方法

诸小亮:既然你已经提前看过了,可否说一说?

张小飞:嗯,我试试吧


张小飞:首先 Map 其实是一个接口,更 Collection 类似,里面定义了很多共性的方法

诸小亮:那,都有哪些方法呢?

张小飞:这可不少,另外因为 Map 是个接口,所以用它的具体实现类 HashMap 演示

1.1. 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);
}

结果:

1.2. putAll

putAll(Map<k,v> map):把指定参数中的数据,都添加到当前 map 中

演示:省略

1.3. get

get(key):根据key获取value

1.4. containsKey

containsKey(key):是否包含指定的key,true:是,false:否

1.5. containsValue

containsValue(value):是否包含指定的value,true:是,false:否

1.6. isEmpty

isEmpty():判断是否空的,true:是,false:否

1.7. remove

remove(key):删除指定的key,返回对应的value

结果:

1.8. clear

clear():清空整个map

结果:

1.9. 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));
    }
}

结果:

1.10. 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());
    }
}

结果:

1.11. values

values():把map中的所有value放到一个集合中返回

结果:


问题:为什么 values() 方法返回的不是 Set 集合?

答案:因为 map 中的 value 可能重复,而 Set 会自动去重

2. HashMap

诸小亮:我看你上面用的都是 HashMap 这个子类,对这个的挺多的啊

张小飞:还好吧,因为传说工作中 HashMap 使用的最频繁,所以一直用这个

诸小亮:嗯,你说的不错,工作中确实它使用的最多,那你能说说它的底层结构吗?

2.1. 数据结构

张小飞:当然可以,数据结构是:可变数组 + 链表,其实 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 后平均查询效率没有红黑树高


2.2. 无序

诸小亮:什么是无序?

张小飞:就是,在 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());
    }
}

结果:

,取出的顺序,跟存放的顺序不同


2.3. 空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"));
}

结果:

3. 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 一样

4. 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 比较器

5. HashTable

张小飞:还有一个HashTable——也是map的具体子类

诸小亮:它更 HashMap 有什么区别?

张小飞:HashMap就是为替代它而生,主要区别:

1. Hashtable 是不允许键或值为 null ,HashMap 的键值都可以为 null
2. HashTable 都是同步方法
3. HashMap 的初始容量为:16,Hashtable 初始容量为:11
4. HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表