当前位置:   article > 正文

Map和Set的详解

map和set

  Map和Set是一种专门用来搜素的容器或者数据结构,其搜索的效率与其具体的实例化子类有关,是一种适合动态查找的集合容器

一、模型

       一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称为Key-Value的键值对 

因此模型会有两种:

   1、纯Key模型

   2、Key-Value模型

其中Map中存储的是Key-Value的键值对,Set中只存储了Key

二、Map的使用

   由此可知:Map是一个接口类,该类没有继承Collection,该类中存储的是<K,V>结构的键值对,并且K是唯一的,不能重复

  1、Map.Entry<K,V>的说明

          Map.Entry<K,V>是Map内部实现的用来存放<Key,Value>键值对映射关系的内部类,该类主要提供了<Key,Value>的获取

方法解释
K.getKey()返回entry中的key
V.getValue()返回entry中的value
V.setValue(V value)将键值对中的value替换为指定value

注: Map.Entry<K,V>并没有提供设置Key的方法

 2、Map的常用方法

注:

    (1) Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap

    (2) Map中存放键值对的Key是唯一的,value是可以重复的

    (3)Map的Key可以全部分离出来,存储到Set中进行访问(因为Key不能重复)

    (4)Map的value可以全部分离出来,存储到Collection中的任意一个子集合中进行访问(因为value不能重复)

    (5)Map中键值对中的Key不能直接修改,value可以修改,如果要修改key,只能先将key删除掉,然后再进行重新插入

    (6)TreeMap和HashMap的区别

Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2^N)O(1)
是否有序关于key有序无序
线程安全不安全不安全
插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址
比较与重写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要重写equals和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能

 3、HashMap的使用案例

  1. public class TestHashMap {
  2. public static void main(String[] args) {
  3. Map<Integer,String> hashMap = new HashMap<>();
  4. hashMap.put(1,"hello");
  5. hashMap.put(3,"world");
  6. hashMap.put(20,"world");
  7. hashMap.put(4,"world");
  8. hashMap.put(5,"world");
  9. System.out.println(hashMap);//输出是无序的
  10. System.out.println(hashMap.getOrDefault(100,"啥也没有"));
  11. //按照指定的key获取value
  12. String v1 = hashMap.get(1);
  13. String v5 = hashMap.get(5);
  14. System.out.println(v1);
  15. System.out.println(v5);
  16. }
  17. }

 4、TreeMap的使用案例

  1. import java.util.Collection;
  2. import java.util.Map;
  3. import java.util.Set;
  4. import java.util.TreeMap;
  5. public class TestTreeMap {
  6. //TreeMap的演示,是有序的
  7. public static void main(String[] args) {
  8. TreeMap<Integer,String> treeMap = new TreeMap<>();
  9. treeMap.put(1,"hello");
  10. treeMap.put(3,"world");
  11. treeMap.put(2,"world");
  12. treeMap.put(4,"world");
  13. treeMap.put(5,"world");
  14. System.out.println(treeMap);
  15. //按照指定的key获取value
  16. String v1 = treeMap.get(1);
  17. String v5 = treeMap.get(5);
  18. System.out.println(v1);
  19. System.out.println(v5);
  20. String v4 = treeMap.getOrDefault(4,"空值");
  21. String v10 = treeMap.getOrDefault(10,"空值");
  22. System.out.println(v4);
  23. System.out.println(v10);
  24. //删除指定的元素
  25. treeMap.remove(5);
  26. System.out.println(treeMap);
  27. //获取所有的key的不重复集合
  28. Set<Integer> keySet = treeMap.keySet();
  29. System.out.println(keySet);
  30. //获取所有的value
  31. Collection<String> values = treeMap.values();
  32. System.out.println(values);
  33. //是否包含key,是否包含value
  34. System.out.println(treeMap.containsKey(1));
  35. System.out.println(treeMap.containsValue("hello"));
  36. //遍历
  37. fun1(treeMap);
  38. System.out.println("方法二");
  39. fun2(treeMap);
  40. }
  41. //根据所有的key去获取值,方法一,不常用
  42. private static void fun1(Map<Integer, String> map) {
  43. Set<Integer> keySet = map.keySet();
  44. for (Integer key : keySet) {
  45. System.out.println("key = " + key + " value = " + map.get(key));
  46. }
  47. }
  48. //方法二
  49. private static void fun2(Map<Integer, String> map) {
  50. Set<Map.Entry<Integer,String>> entries = map.entrySet();
  51. for (Map.Entry<Integer,String> entry : entries) {
  52. System.out.println("key = " + entry.getKey() + " value = " + entry.getValue());
  53. }
  54. }
  55. }

 

三、Set的说明

        Set是继承自Collection的接口类,Set只存储了key

 1、常见方法说明

注:

     (1)Set是继承Collection的一个接口类

     (2) Set只存储了key,并且要求key唯一

     (3)Set底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的

     (4)Set最大的功能就是对集合中的元素进行去重

     (5)实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序

     (6)Set的key不能修改,如果要修改,先把原来的删除,再重新插入

     (7)Set不能插入null的key

     (8)TreeSet和HashSet的区别

Set底层结构TreeSetHashSet
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2^N)O(1)
是否有序关于key有序不一定有序
线程安全不安全不安全
插入/删除/查找区别按照红黑树的特性来进行插入和删除先计算key的哈希地址,然后进行插入和删除
比较与重写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要重写equals和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能

三、搜索树

     二叉搜索树又被称为二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树

    若它的左子树不为空,那么左子树上所有的节点的值都小于根节点的值

    若它的右子树不为空,那么右子树上所有的节点的值都大于根节点的值

    它的左右子树也分别为二叉搜索树

 1、删除操作

       设待删除的结点为cur,双亲结点为parent

        (1)cur.left == null

                    cur是root,则root == cur.right

                    cur不是root,cur是parent.left,则parent.left == cur.right

                    cur不是root,cur是parent.right,则parent.right == cur.right

        (2)cur.right == null

                    cur是root,则root == cur.left

                    cur不是root,cur是parent.left,则parent.left == cur.left

                    cur不是root,cur是parent.right,则parent.right == cur.left

        (3)cur.left != null && cur.right != null

                   需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

  2、实现

  1. public class BinarySearchTree {
  2. private static class TreeNode {
  3. TreeNode left;
  4. TreeNode right;
  5. int val;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. public TreeNode root;
  11. /**
  12. * 查找指定的值是否存在
  13. * @param val
  14. * @return
  15. */
  16. public boolean search(int val) {
  17. return false;
  18. }
  19. /**
  20. * 插入一个元素
  21. * @param val 要插入的值
  22. * @return
  23. */
  24. public boolean insert(int val) {
  25. TreeNode node = new TreeNode(val);
  26. if (root == null) {
  27. root = node;
  28. return true;
  29. }
  30. TreeNode cur = root;
  31. TreeNode pre = null;
  32. while (cur != null) {
  33. if (val == cur.val) {
  34. return false;
  35. }
  36. pre = cur;
  37. if (val < cur.val) {
  38. cur = cur.left;
  39. } else {
  40. cur = cur.right;
  41. }
  42. }
  43. if (val < pre.val) {
  44. pre.left = node;
  45. } else {
  46. pre.right = node;
  47. }
  48. return true;
  49. }
  50. public boolean remove(int val) {
  51. if (root == null) {
  52. return false;
  53. }
  54. TreeNode cur = root;
  55. TreeNode parent = null;
  56. while (cur != null) {
  57. if (cur.val == val) {
  58. removeNode(parent,cur);
  59. return true;
  60. }
  61. parent = cur;
  62. if (cur.val > val) {
  63. cur = cur.left;
  64. } else {
  65. cur = cur.right;
  66. }
  67. }
  68. return false;
  69. }
  70. private void removeNode(TreeNode parent,TreeNode cur) {
  71. if (cur.left == null) {
  72. if (cur == root) {
  73. root = cur.right;
  74. } else if (cur == parent.left) {
  75. //当前节点是父节点的左孩子节点时
  76. parent.left = cur.right;
  77. } else {
  78. //当前节点是父节点的右孩子节点时
  79. parent.right = cur.right;
  80. }
  81. } else if (cur.right == null) {
  82. if (cur == root) {
  83. root = cur.left;
  84. } else if (cur == parent.left) {
  85. //当前节点是父节点的左孩子节点时
  86. parent.left = cur.left;
  87. } else {
  88. //当前节点是父节点的右孩子节点时
  89. parent.right = cur.left;
  90. }
  91. } else {
  92. TreeNode target = cur.right;
  93. TreeNode parentTarget = cur;
  94. while (target.left != null) {
  95. parentTarget = target;
  96. target = target.left;
  97. }
  98. //到达叶子节点
  99. cur.val = target.val;
  100. //删除target节点
  101. if (target == parentTarget.left) {
  102. parentTarget.left = target.right;
  103. } else {
  104. parentTarget.right = target.right;
  105. }
  106. }
  107. }
  108. public String inorder(TreeNode node) {
  109. StringBuilder sb = new StringBuilder();
  110. if (node == null) {
  111. return sb.toString();
  112. }
  113. String left =inorder(node.left);
  114. sb.append(left);
  115. sb.append(node.val + " ");
  116. String right = inorder(node.right);
  117. sb.append(right);
  118. return sb.toString();
  119. }
  120. public boolean search1(int val) {
  121. if (root == null) {
  122. return false;
  123. }
  124. TreeNode cur = root;
  125. while (cur != null) {
  126. if (val == cur.val) {
  127. return true;
  128. }
  129. if (val < cur.val) {
  130. cur = cur.left;
  131. } else {
  132. cur = cur.right;
  133. }
  134. }
  135. return false;
  136. }
  137. }

四、练习题加深理解

  1. import java.util.*;
  2. public class Exe_01 {
  3. public static void main(String[] args) {
  4. //生成十万个元素的数组
  5. int capacity = 10000;
  6. int[] arr = new int[capacity];
  7. Random random = new Random();
  8. for (int i = 0;i < arr.length;i++) {
  9. int value = random.nextInt(capacity);
  10. arr[i] = value;
  11. }
  12. fun1(arr);
  13. fun2(arr);
  14. fun3(arr);
  15. }
  16. //去除十万个元素中重复的元素
  17. public static void fun1(int[] arr) {
  18. Set<Integer> set = new HashSet<>();
  19. for (int i = 0;i < arr.length;i++) {
  20. set.add(arr[i]);
  21. }
  22. System.out.println("去重后元素个数" + set.size());
  23. }
  24. //查找十万个元素中第一次重复的元素
  25. public static void fun2(int[] arr) {
  26. Set<Integer> set = new HashSet<>();
  27. for (int i = 0;i < arr.length;i++) {
  28. if (!set.contains(arr[i])) {
  29. set.add(arr[i]);
  30. } else {
  31. System.out.println("第一个重复的元素" + arr[i]);
  32. return;
  33. }
  34. }
  35. }
  36. //统计十万个元素中,每个元素出现的次数
  37. public static void fun3(int[] arr) {
  38. Map<Integer, Integer> map = new HashMap<>();
  39. for (int i = 0;i < arr.length;i++) {
  40. int cnt = map.getOrDefault(arr[i],0);
  41. map.put(arr[i],cnt + 1);
  42. }
  43. }
  44. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/570871
推荐阅读
相关标签
  

闽ICP备14008679号