一、四大函数式接口以及Stream流,详见 【补充:Stream流】

二、Map集合

1、Map集合的常用方法

  1. package com.qfedu.day12_02;
  2. import java.util.Collection;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Set;
  6. import java.util.function.BiFunction;
  7. /**
  8. * @Author laoyan
  9. * @Description TODO
  10. * @Date 2022/3/16 10:56
  11. * @Version 1.0
  12. */
  13. public class DemoMap {
  14. /**
  15. * map集合的常用方法
  16. * @param args
  17. */
  18. public static void main(String[] args) {
  19. /**
  20. * Map 是一个接口,父接口不是Collection
  21. * Map 接口他的有名的实现类 HashMap
  22. * 他跟之前的集合相比,最大的不同就是 是以键值对形式出现的。
  23. * Map 集合又称之为本地缓存(jvm内存中的)。
  24. */
  25. Map<String, String> map = new HashMap<>();
  26. // put 存放数据, 而且有返回值,如果key已经存在,就覆盖掉原来的value,并且返回被覆盖掉的值,如果不是覆盖,返回null
  27. map.put("100","张三");
  28. map.put("200","李四");
  29. map.put("300","王五");
  30. // get 通过key 值获取 value
  31. System.out.println(map.get("100"));
  32. // 如果出现相同的key 值,覆盖原来的数据
  33. String name = map.put("100", "老闫");
  34. String name2 = map.put("400", "老闫");
  35. System.out.println(name);
  36. System.out.println(name2);
  37. // null 可以当做 map集合中的key 和 value
  38. map.put(null,"慕遥");
  39. map.put("500",null);
  40. System.out.println(map);
  41. System.out.println(map.get(null));
  42. // put 是覆盖,putIfAbsent 是存在就插入不了了,不是覆盖,而且返回已经存在的值
  43. String name3 = map.putIfAbsent("400", "张宇");// 打印出来是老闫
  44. System.out.println(name3);
  45. HashMap<String, String> m = new HashMap<>();
  46. m.put("181","张局长");
  47. m.put("188","黄主任");
  48. m.put("400","闫政委");
  49. map.putAll(m);
  50. System.out.println(map);
  51. // map.remove("188"); // 通过Key 删除键值对
  52. map.remove("188","黄主任"); // 通过key 和 value 删除键值对
  53. System.out.println(map);
  54. //map.clear();
  55. boolean b = map.replace("181", "张局长", "张军长");
  56. System.out.println(map);
  57. map.replaceAll(new BiFunction<String, String, String>() {
  58. @Override
  59. public String apply(String key, String value) {
  60. System.out.println("k="+key);
  61. System.out.println("v="+value);
  62. if(null != key && Integer.valueOf(key) > 300){
  63. return "hello" + value;
  64. }else{
  65. return "world";
  66. }
  67. }
  68. });
  69. System.out.println(map.get("1000")); // null
  70. System.out.println(map.getOrDefault("1000","ABC")); // 如果获取不到,直接返回默认值ABC,如果获取到了,直接返回value
  71. System.out.println(map);
  72. System.out.println(map.isEmpty()); // list , String , Set
  73. System.out.println(map.size()); // 返回map集合中键值对的个数
  74. System.out.println(map.containsKey("400")); // true
  75. System.out.println(map.containsValue("闫政委")); // false
  76. Set<String> keys = map.keySet(); // map 中所有的键,都存放在一个set中
  77. for (String key:keys) {
  78. System.out.println(key);
  79. }
  80. Collection<String> values = map.values();
  81. for (String value:values) {
  82. System.out.println(value);
  83. }
  84. }
  85. }

2、Map集合的三种遍历方式

package com.qfedu.day12_02;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/16 11:41
 * @Version 1.0
 */
public class DempMap2 {
    /**
     *   map集合的三种遍历方式
     * @param args
     */
    public static void main(String[] args) {
        HashMap<String, String> m = new HashMap<>();
        m.put("181","张局长");
        m.put("188","黄主任");
        m.put("400","闫政委");
        /**
         *  获取里面所有的键值对
         *  通过key 获取value
         */
        Set<String> keys = m.keySet();
        for (String key:keys) {
            System.out.println("Key="+key+",value ="+m.get(key));
        }
        /**
         *  第二种方式 forEach
         */
        m.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println("Key="+key+",value ="+m.get(key));
            }
        });

        m.forEach((key,value) ->System.out.println("Key="+key+",value ="+m.get(key)));

        /**
         *  第三种:使用Entry 获取 key  value
         *   class Entry{
         *       private String key;
         *       private String value
         *   }
         *   Set 集合
         *   {entry1,entry2......}
         */
        Set<Map.Entry<String, String>> entrySet = m.entrySet();
        for (Map.Entry<String,String> entry:entrySet) {
            System.out.println("Key="+entry.getKey()+",value ="+entry.getValue());
        }


    }
}

3、Hashtable

1. HashMap是线程不安全的集合, Hashtable是线程安全的集合。
2. HashMap允许出现null键值, Hashtable是不允许的。
3. HashMap的父类是AbstractMap, Hashtable的父类是Dictionary。
4. HashMap的Map接口的新的实现类, 底层算法效率优于Hashtable。

4、TreeMap

TreeMap 是有序的,指的是Key有序。

5、LinkedHashMap

HashMap  -->  LinkedHashMap
HashMap 中存储了多个元素,打印输出的时候,不按照我存入的顺序打印。
LinkedHashMap 打印就是按照存储的顺序来的。

代码:

package com.qfedu.day12_02;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.TreeMap;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/16 14:59
 * @Version 1.0
 */
public class DemoTreeMap {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(1,"我们的奥运");
        treeMap.put(5,"这个杀手不太冷静");
        treeMap.put(3,"长津湖之水门桥");
        treeMap.put(4,"守岛人");
        System.out.println(treeMap);

        TreeMap<Film, Integer> treeMap2 = new TreeMap<>();
        treeMap2.put(new Film("长津湖","陈凯歌"),20);
        treeMap2.put(new Film("功夫2","周星驰"),10);
        treeMap2.put(new Film("阿发达","老闫"),200);
        System.out.println(treeMap2);

        // Map 集合中的key 值一般都是String或者int 类型的, 很少是自定义的类的类型

        HashMap<Integer, String> treeMap3 = new HashMap<>();
        treeMap3.put(1,"我们的奥运");
        treeMap3.put(5,"这个杀手不太冷静");
        treeMap3.put(3,"长津湖之水门桥");
        treeMap3.put(4,"守岛人");
        System.out.println(treeMap3);


        LinkedHashMap<Integer, String> treeMap4 = new LinkedHashMap<>();
        treeMap4.put(1,"我们的奥运");
        treeMap4.put(5,"这个杀手不太冷静");
        treeMap4.put(3,"长津湖之水门桥");
        treeMap4.put(4,"守岛人");
        System.out.println(treeMap4);

        HashSet<String> set = new HashSet<>();
        // 通过这个源码,我们看到HashSet 底层使用了HashMap
        set.add("zahgnsan");

    }
}

6、HashMap的存储原理

HashSet 底层数据结构是哈希表(JDK8里面:数组+链表+红黑树,JDK8之前:数组+链表)
JDK1.8之前结构:数组+链表
image.png
JDK1.8之后结构:节点数少于8个的时候采用数组+链表的结构,当节点数多于8个的时候,采用的是数组+红黑树的结构 。
HashMap存储的数据存放在内存中,提高HashMap数据寻址速度是重点要解决的问题,所以HashMap底层的存储结构非常关键,如果使用数组存储,时间复杂度为O(1),使用链表存储,时间复杂度为O(n),如果使用二叉树存储,时间复杂度为O(lg(n))。所以HashMap优先使用数组存储,如果出现hash碰撞,采用链表存储,如果链表长度大于8,寻址速度明显下降,进一步采用红黑树存储,将寻址效率提高。
image.png
当存放两个元素获取Hash并且Mod 16得到Hash桶编号相同时,出现Hash碰撞,hash桶使用链表存储这两个对象,使用HashMap.get(key)获取这两个对象时,需要两步实现,首先得到它们对应的Hash桶号,在根据Key与链表中的Key1=Value1比较key.equale(key1),找出对应元素,这样需要递归遍历列表中的元素,执行效率降低。当链表长度大于8时,HashMap将链表存储转化为红黑树存储,红黑树遍历每次可以减少一个分支,效率比链表高,其时间复杂度为O(lg(n))。

7、HashMap 的扩容规则

HashMap使用数组存储,默认长度为16,加载因子为0.75,当HashMap的Hash桶数量的75%被分配使用时,HashMap扩容为原来的两倍。 跟HashSet一样。