核心原理

底层实现

  • 底层采用哈希表,一种非常重要的数据结构,redis数据库的核心技术和HashMap一样
  • 数据结构中数组加链表对数据进行存储时,各有特点:
    1. 数组占用空间连续。寻址容易,查询速度快,但是增加和删除效率非常低
    2. 链表占用空间不连续。寻址困难,查询速度慢,但是增删效率非常高
  • 哈希表结合了数组和链表的优点,查询快,增删效率也高。其本质是“数组+链表”
  • 一个Entry对象存储内容如下
    • key 键对象 value 值对象
    • next 下一个节点
    • hash 键对象的hash值
  • 每一个Entry对象就是一个单向链表结构,使用图形表示一个Entry对象的典示例

image.png
image.png
image.png

手写示例

  1. package collection;
  2. /**
  3. * 自定义接口MyMap,实现Map部分内容
  4. * @pram K,V
  5. */
  6. public interface MyMap<K,V> {
  7. int size();
  8. boolean isEmpty();
  9. boolean containsKey(Object key);
  10. boolean containsValue(Object value);
  11. V get(K key);
  12. void put(K key, V value);
  13. void remove(Object key);
  14. }
  1. package collection;
  2. public class MyHashMap<K,V> implements MyMap<K,V>{
  3. private final static int INIT_CAPACITY = 1<<4;
  4. private int size;
  5. private Entry[] table;
  6. public MyHashMap() {
  7. table = new Entry[INIT_CAPACITY];
  8. }
  9. public static void main(String[] args) {
  10. MyHashMap<String,String> hm = new MyHashMap<>();
  11. hm.put("name","zhangsan");
  12. hm.put("age","13");
  13. hm.put("sex","male");
  14. hm.put("career","home");
  15. hm.put("career","car");
  16. System.out.println(hm.get("sex"));
  17. System.out.println(hm.toString());
  18. hm.remove("age");
  19. System.out.println(hm.toString());
  20. }
  21. private int hash(K key) {
  22. int hashVal = key.hashCode();
  23. hashVal = (hashVal<0)?-hashVal:hashVal;
  24. return hashVal & (table.length-1);
  25. // return hashVal & (1);
  26. }
  27. @Override
  28. public int size() {
  29. return size;
  30. }
  31. @Override
  32. public boolean isEmpty() {
  33. return size == 0;
  34. }
  35. @Override
  36. public boolean containsKey(Object key) {
  37. return false;
  38. }
  39. @Override
  40. public boolean containsValue(Object value) {
  41. return false;
  42. }
  43. @Override
  44. public V get(K key) {
  45. int index = hash(key);
  46. Entry e = table[index];
  47. while (e != null){
  48. if (e.key.equals(key)){
  49. return (V)e.value;
  50. }
  51. e = e.next;
  52. }
  53. return null;
  54. }
  55. @Override
  56. public void put(K key, V value) {
  57. int index = hash(key);
  58. Entry en = new Entry<>(index,key,value,null);
  59. if (table[index] == null){
  60. table[index] = en;
  61. }else{
  62. Entry pos = table[index];
  63. Entry last = pos;
  64. while (pos!=null) {
  65. if (pos.key.equals(key)){
  66. pos.value = value;
  67. return;
  68. }
  69. last = pos;
  70. pos= pos.next;
  71. }
  72. last.next = en;
  73. }
  74. size++;
  75. return;
  76. }
  77. @Override
  78. public void remove(K key) {
  79. int index = hash(key);
  80. Entry e = table[index];
  81. Entry prevous = null;
  82. while (e != null) {
  83. if (e.key.equals(key)) {
  84. if (prevous == null) {
  85. // 说明是第0个元素
  86. e = e.next;
  87. } else {
  88. // 说明是第n个元素
  89. prevous.next = e.next;
  90. }
  91. size --;
  92. return;
  93. }
  94. prevous = e;
  95. e = e.next;
  96. }
  97. return;
  98. }
  99. @Override
  100. public String toString() {
  101. StringBuilder sb = new StringBuilder();
  102. sb.append("[");
  103. for (Entry entry:table) {
  104. while(entry!=null){
  105. sb.append("{"+entry.key + ":" + entry.value + "}");
  106. if (entry.next!=null){
  107. sb.append(",");
  108. }
  109. entry = entry.next;
  110. }
  111. }
  112. sb.append("]");
  113. return sb.toString();
  114. }
  115. }
  116. class Entry<K,V>{
  117. int hash;
  118. K key;
  119. V value;
  120. Entry next;
  121. public Entry(int hash, K key, V value, Entry next) {
  122. this.hash = hash;
  123. this.key = key;
  124. this.value = value;
  125. this.next = next;
  126. }
  127. public Entry() {}
  128. }

HashSet

  1. package collection;
  2. public class MyHashSet<E> {
  3. MyHashMap map;
  4. public static final Object DEFAULT_OBJ = new Object();
  5. public MyHashSet() {
  6. map = new MyHashMap<>();
  7. }
  8. public static void main(String[] args) {
  9. MyHashSet<String> set = new MyHashSet();
  10. set.add("aaa");
  11. set.add("bbb");
  12. set.add("aaa");
  13. }
  14. private void add(E val) {
  15. map.put(val,DEFAULT_OBJ);
  16. }
  17. public int size() {
  18. return map.size();
  19. }
  20. }

知识点

  • equals和hashcode通常必须在一起重写
  • equals为true,那么hashcode必须相等,这么处理就是为了在HashMap的存取中不会出现悖论,如果hashcode不一致,在hashmap中就无法定位到正确的entry去进行比较
    1. int index = hash(key);