Java实现HashTable

简介: 哈希表(Hash Table)也叫做散列表,是根据键值对(Key Value)而直接访问的数据结构。它通过将关键码值Key映射到表的一个位置来直接访问,以此加快查找的速度。这个映射函数叫做散列函数,存放记录的数值叫做散列表。

Google面试问题描述

有一个公司, 当有新员工报道的时候, 要求将该员工的信息保存(id, 姓名等), 当输入该员工的的id时, 要求查找该员工的所有信息。
注: 不要使用数据库, 尽量节省内存, 速度越快越好

思路分析

不让使用数据库, 越快越好, 我们选择哈希表
使用链表来实现哈希表, 该链表不带表头, 即从链表的第一个结点就开始存放雇员信息

什么是哈希表

一、定义

HashTable在Java中的定义如下:

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable

从中可以看出HashTable继承Dictionary类,实现Map接口。其中Dictionary类是任何可将键映射到相应值的类(如 Hashtable)的抽象父类。每个键和每个值都是一个对象。在任何一个 Dictionary 对象中,每个键至多与一个值相关联。Map是"key-value键值对"接口。

HashTable采用"拉链法"实现哈希表,它定义了几个重要的参数:table、count、threshold、loadFactor、modCount。

table: 为一个Entry[]数组类型,Entry代表了“拉链”的节点,每一个Entry代表了一个键值对,哈希表的"key-value键值对"都是存储在Entry数组中的。

count: HashTable的大小,注意这个大小并不是HashTable的容器大小,而是他所包含Entry键值对的数量。

threshold: Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。

loadFactor: 加载因子。

modCount: 用来实现“fail-fast”机制的(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你(你已经出错了)。

二、主要方法

HashTable的API对外提供了许多方法,这些方法能够很好帮助我们操作HashTable,但是这里我只介绍两个最根本的方法:put、get

首先我们先看put方法:将指定 key 映射到此哈希表中的指定 value。注意这里键key和值value都不可为空。

Put:

put方法的整个处理流程是:计算key的hash值,根据hash值获得key在table数组中的索引位置,然后迭代该key处的Entry链表(我们暂且理解为链表),若该链表中存在一个这个的key对象,那么就直接替换其value值即可,否则在将改key-value节点插入该index索引位置处。如下:

首先我们假设一个容量为5的table,存在8、10、13、16、17、21。他们在table中位置如下:
在这里插入图片描述
然后我们插入一个数:put(16,22),key=16在table的索引位置为1,同时在1索引位置有两个数,程序对该“链表”进行迭代,发现存在一个key=16,这时要做的工作就是用newValue=22替换oldValue16,并将oldValue=16返回。

在这里插入图片描述

在put(33,33),key=33所在的索引位置为3,并且在该链表中也没有存在某个key=33的节点,所以就将该节点插入该链表的第一个位置。
在这里插入图片描述


Get:

相对于put方法,get方法就会比较简单,处理过程就是计算key的hash值,判断在table数组中的索引位置,然后迭代链表,匹配直到找到相对应key的value,若没有找到返回null。

三、HashTable与HashMap的区别

HashTable和HashMap存在很多的相同点,但是他们还是有几个比较重要的不同点。

第一: 我们从他们的定义就可以看出他们的不同,HashTable基于Dictionary类,而HashMap是基于AbstractMap。Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。

第二: HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。如下:

当HashMap遇到为null的key时,它会调用putForNullKey方法来进行处理。对于value没有进行任何处理,只要是对象都可以。

第三: Hashtable的方法是同步的,而HashMap的方法不是。所以有人一般都建议如果是涉及到多线程同步时采用HashTable,没有涉及就采用HashMap,但是在Collections类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的Map对象,并把它作为一个封装的对象来返回,所以通过Collections类的synchronizedMap方法是可以我们你同步访问潜在的HashMap。

具体用代码实现

代码中注释的很清楚了,每个方法的作用
import java.util.Scanner;
public class HashTabDemo {

    public static void main(String[] args) {
        HashTab hashTab = new HashTab(7);
        String key = "";
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("put:    添加雇员信息");
            System.out.println("list:   展示雇员信息");
            System.out.println("search: 查找雇员信息");
            System.out.println("move:   删除雇员信息");
            System.out.println("exit:   退出程序!");
            key = scanner.next();
            switch (key) {
                case "put":
                    System.out.printf("请输入用户id:");
                    int id = scanner.nextInt();
                    System.out.println();
                    System.out.printf("请输入用户名:");
                    String name = scanner.next();
                    hashTab.addEmp(new Emp(id, name));
                    break;
                case "list":
                    hashTab.list();
                    break;
                case "search":
                    System.out.printf("请输入用户id");
                    id = scanner.nextInt();
                    hashTab.findEmdByid(id);
                    break;
                case"move":
                    System.out.printf("请输入要删除用户的id:");
                    id=scanner.nextInt();
                    hashTab.moveEmpByid(id);
                    break;
                case "exit":
                    scanner.close();
                    System.exit(0);
                    break;
                default:
                    System.out.println("输入信息有误,请重新输入!");
            }
        }
    }
}


class HashTab {
    private EmpLinkList[] empLinkLists;
    private int size;

    public HashTab(int size) {
        this.size = size;
        empLinkLists = new EmpLinkList[size];
        for (int i = 0; i < size; i++) {
            empLinkLists[i] = new EmpLinkList();
        }
    }

    //添加员工信息
    public void addEmp(Emp emp) {
        empLinkLists[hashFun(emp.id)].addEmp(emp);
    }

    //散列函数  取膜法,判断应该加到那个链表中去
    public int hashFun(int id) {
        return id % size;
    }

    //遍历所有链表的员工信息
    public void list() {
        for (int i = 0; i < size; i++) {
            empLinkLists[i].listEmp(i);
            System.out.println();
        }
    }

    //查找雇员
    public void findEmdByid(int id) {
        Emp emp = empLinkLists[hashFun(id)].findEmpById(id);
        if (emp == null) {
            System.out.println("该雇员不存在。");
        } else {
            System.out.println("该雇员的信息为:id=" + emp.id + " name=" + emp.name);
        }
    }

    //删除员工
    public void moveEmpByid(int id) {
        Boolean is = empLinkLists[hashFun(id)].moveEmpById(id);
        if (is){
            System.out.println("删除雇员成功!");
        }else {
            System.out.println("哈希表中不存在该雇员信息。");
        }
    }


}


//管理员工信息的链表
class EmpLinkList {
    private Emp head;

    //添加员工的操作
    public void addEmp(Emp emp) {
        //如果头指针为空,则表明链表中还没有数据
        if (head == null) {
            head = emp;
        } else {
            Emp curEmp = head;
            while (curEmp.next != null) {
                curEmp = curEmp.next;
            }
            curEmp.next = emp;
        }
    }


    //显示该链表中所有员工的操作
    public void listEmp(int no) {
        if (head == null) {
            System.out.printf("第 " + (no + 1) + " 条链表为空。");
            return;
        }
        System.out.printf("第 " + (no + 1) + " 条链表信息为:");
        Emp curEmp = head;
        while (true) {
            System.out.printf("=> id=" + curEmp.id + " name=" + curEmp.name + "\t");
            if (curEmp.next != null) {
                curEmp = curEmp.next;
            } else {
                break;
            }
        }
    }


    //通过id查找雇员的信息
    public Emp findEmpById(int id) {
        if (head == null) {
            return null;
        }
        Emp curEmp = head;
        while (true) {
            if (curEmp.id == id) {
                return curEmp;
            }
            if (curEmp.next == null) {
                break;
            }
            curEmp = curEmp.next;
        }
        return null;
    }

    //通过id删除雇员信息
    public Boolean moveEmpById(int id) {
        if (head == null) {
            return false;
        } else if (head.id == id) {
            //首先判断链表的第一个是不是该元素
            head = head.next;
            return true;
        } else if (head.next == null) {
            return false;
        } else {
            Emp preEmp = head;
            Emp rearEmp = head.next;
            while (true) {
                if (rearEmp.id == id) {
                    preEmp.next = rearEmp.next;
                    rearEmp.next = null;
                    return true;
                }
                if (rearEmp.next == null) {
                    break;
                }
                rearEmp = rearEmp.next;
                preEmp = preEmp.next;
            }
        }

        return false;
    }


}


//员工信息类
class Emp {
    public int id;
    public String name;
    public Emp next;

    public Emp(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
目录
相关文章
|
4月前
|
存储 安全 算法
详解Java中HashMap、HashTable、ConcurrentHashMap常见问题
详解Java中HashMap、HashTable、ConcurrentHashMap常见问题
45 0
|
4天前
|
存储 安全 Java
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
|
14天前
|
存储 Java 索引
【亮剑】Java中的并发容器ConcurrentHashMap,它在JDK1.5中引入,用于替换HashTable和SynchronizedMap
【4月更文挑战第30天】本文介绍了Java中的并发容器ConcurrentHashMap,它在JDK1.5中引入,用于替换HashTable和SynchronizedMap。文章展示了创建、添加、获取、删除和遍历元素的基本用法。ConcurrentHashMap的内部实现基于分段锁,每个段是一个独立的Hash表,通过分段锁实现并发控制。每个段内部采用数组+链表/红黑树的数据结构,当冲突过多时转为红黑树优化查询。此外,它有扩容机制,当元素超过阈值时,会逐段扩容并翻倍Segment数量,以保持高性能的并发访问。
|
17天前
|
存储 安全 Java
【JAVA】concurrentHashMap和HashTable有什么区别
【JAVA】concurrentHashMap和HashTable有什么区别
|
3月前
|
存储 并行计算 安全
【Java编程进阶之路 01】深入探索:HashMap、ConcurrentHashMap与HashTable的演进之路
HashMap、ConcurrentHashMap与HashTable均为Java中的哈希表实现。HashMap非线程安全但性能高,适用于单线程;HashTable线程安全但性能较低,已少用;ConcurrentHashMap线程安全且高性能,是并发环境下的首选。三者在线程安全性与性能间各有侧重。
|
9月前
|
存储 安全 Java
java学会这些,我就入门啦!(基础篇六)HashMap、Hashtable、ConcurrentHashMap的原理与区别
java学会这些,我就入门啦!(基础篇六)HashMap、Hashtable、ConcurrentHashMap的原理与区别
|
5月前
|
存储 安全 Java
Java集合框架:HashMap和HashTable的区别是什么?
Java集合框架:HashMap和HashTable的区别是什么?
28 0
|
7月前
|
存储 缓存 安全
【JavaSE专栏55】Java集合类HashTable解析,基于哈希表实现的唯一性键值对存储数据结构
【JavaSE专栏55】Java集合类HashTable解析,基于哈希表实现的唯一性键值对存储数据结构
|
9月前
|
存储 安全 Java
java 之Hashtable
当涉及到在 Java 中存储和管理键值对数据时,`Hashtable` 是一个重要且值得探讨的工具。作为 Java 集合框架中的一员,`Hashtable` 提供了一种有序、高效的数据存储方式,可以满足许多开发场景的需求。在本文中,我们将深入探讨 Java 中的 `Hashtable`,了解其特点、用法以及何时适用。
|
9月前
|
Java
Java 中Map接口及其实现子类HashMap,Hashtable,Properties,TreeMap类的详解(二)
Java 中Map接口及其实现子类HashMap,Hashtable,Properties,TreeMap类的详解
26 0