散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
    给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

    在java中 hashMap 的底层就是数组+链表+红黑树。所以我们可以采用数组+链表来手写一个哈希表。

    而我们一般的哈希函数都用除留余数法
    H(key)=key MOD p (p<=m m为表长),key与表长做模运算
    解决冲突的方法使用链地址法。
    image.png
    这里举个例子就是,看这张图
    一共有数组一共16个元素。每个数组后边跟了一个链表,坦白了讲就是链表类型的数组。
    现在我们需要存储 496
    496 % 16 = 0,所以我们放在了下标为0这个位置链表上
    我们需要存储896的时候,896 % 16 = 0,同样的我们也加在下标为0的链表上。
    当我们需要存储1的时候,1 % 16 = 1,所以我们把他放在下标为1的链表上。

    具体的散列函数和解决冲突的方法可以去哈希表百度百科了解

    下边的代码实现也很简单,只要你的单链表掌握的不错,那么哈希表对你来讲也是没问题的。

    1. public class HashTabTest {
    2. public static void main(String[] args) {
    3. Employee hash = new Employee(1, "halo");
    4. Employee hi = new Employee(2, "hi");
    5. Employee qi = new Employee(7, "qi");
    6. Employee hello = new Employee(1024, "hello");
    7. HashTab hashTab = new HashTab(6);
    8. hashTab.add(hash);
    9. hashTab.add(hi);
    10. hashTab.add(qi);
    11. hashTab.add(hello);
    12. hashTab.list();
    13. System.out.println(hashTab.find(13));
    14. hashTab.list();
    15. }
    16. }
    17. class HashTab {
    18. private EmployeeLinked[] linked;
    19. private Integer size;
    20. public HashTab(Integer size) {
    21. this.size = size;
    22. linked = new EmployeeLinked[size];
    23. //对数组中每个元素初始化
    24. for (int i = 0; i < size; i++) {
    25. linked[i] = new EmployeeLinked();
    26. }
    27. }
    28. public void add(Employee emp) {
    29. //首先使用散列函数根据传入的 emp.id ,计算存储在哪条链表
    30. int no = emp.id % size;
    31. linked[no].add(emp);
    32. }
    33. public void list() {
    34. //遍历的时候要遍历所有的数组链表
    35. for (int i = 0; i < size; i++) {
    36. linked[i].list(i);
    37. }
    38. }
    39. public Employee find(int id) {
    40. int no = id % size;
    41. return linked[no].findEmpById(id);
    42. }
    43. }
    44. //employee 链表
    45. class EmployeeLinked {
    46. //头节点,指向第一个节点
    47. private Employee head;
    48. //向链表中添加新元素
    49. public void add(Employee node) {
    50. //如果头节点为null,那现在加入的元素就是头一个元素
    51. if (head == null) {
    52. head = node;
    53. return;
    54. }
    55. //不是第一个节点,那么我们就要定义一个辅助变量来找到最后一个节点
    56. Employee temp = head;
    57. //循环遍历到最后一个节点
    58. //如果temp.next == null,说明当前节点为最后一个节点
    59. while (temp.next != null) {
    60. // temp 指针后移,指向下一个节点
    61. temp = temp.next;
    62. }
    63. //将最后一个节点 next指向新的元素
    64. temp.next = node;
    65. }
    66. //遍历链表
    67. public void list(int i) {
    68. System.out.print("第"+ i +"条链表");
    69. if (head == null){
    70. System.out.println("->null");
    71. return;
    72. }
    73. Employee temp = head;
    74. while (temp != null) {
    75. System.out.print("->" + temp);
    76. temp = temp.next;
    77. }
    78. System.out.println();
    79. }
    80. public Employee findEmpById(int id) {
    81. if (head == null) {
    82. return null;
    83. }
    84. Employee temp = head;
    85. while (true) {
    86. if (temp.id == id) {
    87. return temp;
    88. }
    89. if (temp.next == null) {
    90. return null;
    91. }
    92. temp = temp.next;
    93. }
    94. }
    95. }
    96. class Employee {
    97. Integer id;
    98. String name;
    99. Employee next;
    100. public Employee(Integer id, String name) {
    101. this.id = id;
    102. this.name = name;
    103. }
    104. @Override
    105. public String toString() {
    106. return "Employee{" +
    107. "id=" + id +
    108. ", name='" + name + '\'' +
    109. '}';
    110. }
    111. }