Java模拟哈希表并简单的使用

Java模拟哈希表并简单的使用

基本介绍

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列

在这里插入图片描述
​可见,哈希表/散列表这种数据结构的特点:是一个一维数组,这个数组中的每个元素是一个单向链表;(数组和链表的结合体);数组:在查询方面效率高,但是在随机增删方面效率很低;单向链表:在随机增删方面效率较高,但是在查询方面效率很低;哈希表/散列表就是将以上两种数据结构融合在一起!!!

散列分布均匀的概念
​假设有100个元素,其最好能划分为10个单向链表,每个单向链表上有10个元素,这种情况是最好的,是散列分布均匀的;

哈希碰撞的概念
如果两个对象a1和a2的哈希值相同,这两个一定是在同一个链表上的(哈希值会经过哈希算法/哈希函数 转换成数组下标,对应的就是某个链表);注意:当a1和a2的哈希值不同,这两个任然有可能在同一个链表上,因为两个不同的哈希值经过哈希算法/哈希函数 有可能会转换为同一个数组下标(这就是 “哈希碰撞”);

简单应用

假设有许多的用户,如何将这些用户信息使用哈希表保存起来,并且能够实时查询、修改、删除、或者添加新的用户?
数据类User的创建

package edu.hebeu.hashtable;

public class User {
	
	private int id;
	
	private String name;
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + "]";
	}
	
}

存放数据的节点类Node

package edu.hebeu.hashtable;

public class Node {
	
	public Node next;
	
	public User data;
	
	public Node(User data) {
		this.data = data;
	}
}

单链表类LinkedList的创建(这里来个不带头节点的,因为我前面写的都是带头节点的)

package edu.hebeu.hashtable;

public class LinkedList {
	
	private Node head;
	
	/**
	 * 尾插法
	 * @param node
	 */
	public void tailInsert(Node node) {
		if (head == null) {
			head = node;
			return;
		}
		if(this.select(node) != null) {
			System.err.println("已存在id为" + node.data.getId() + "的用户,添加失败!");
			return;
		}
		Node lastNode = head;
		while(true) {
			if(lastNode.next == null) {
				break;
			}
			lastNode = lastNode.next;
		}
		lastNode.next = node;
	}
	
	/**
	 * 删除节点
	 * @param node
	 */
	public void del(Node node) {
		if (head == null) {
			return;
		}
		if(head.data.getId() == node.data.getId()) { // 如果删除的元素为链表的第一个节点
			head = head.next;
			return;
		}
		Node preNode = head;
		while (true) {
			if (preNode.next.data.getId() == node.data.getId()) {
				preNode.next = preNode.next.next;
				break;
			}
			if (preNode.next == null) {
				break;
			}
			preNode = preNode.next;
		}
	}
	
	/**
	 * 修改节点
	 * @param node
	 */
	public void update(Node node) {
		if (head == null) {
			return;
		}
		Node currentNode = head;
		while (true) {
			if (currentNode == null) {
				break;
			}
			if (currentNode.data.getId() == node.data.getId()) {
				currentNode.data = node.data;
				break;
			}
			currentNode = currentNode.next;
		}
	}
	
	/**
	 * 查询节点
	 * @param node
	 * @return
	 */
	public Node select(Node node) {
		if(head == null) {
			return null;
		}
		Node currentNode = head;
		while(true) {
			if (currentNode == null) {
				break;
			}
			if (currentNode.data.getId() == node.data.getId()) {
				return currentNode;
			}
			currentNode = currentNode.next;
		}
		return null;
	}
	
	/**
	 * 打印链表
	 */
	public void show() {
		if (head == null) {
			return;
		}
		Node currentNode = head;
		while (true) {
			if(currentNode == null) {
				break;
			}
			System.out.print(currentNode.data + " -> ");
			currentNode = currentNode.next;
		}
	}
	
}

哈希表类HashTable的创建,这里我们用最简单的通过User类的id取模法来决定改对象放到哪个链表

package edu.hebeu.hashtable;

/**
 * 哈希表
 * @author 13651
 *
 */
public class MyHashTable {
	
	/**
	 * 哈希表的每一条链表,即数组每个元素对应的链表
	 */
	private LinkedList[] linLists;
	/**
	 * 哈希表的大小,即数组长度
	 */
	private int size;
	
	public MyHashTable(int size) {
		this.size = size;
		linLists = new LinkedList[size];
		for (int i = 0; i < size; i++) {
			linLists[i] = new LinkedList();
		}
	}
	
	/**
	 * 添加用户的方法
	 * @param user
	 */
	public void add(User user) {
		int linListNum = hashFun(user.getId()); // 通过添加的用户的id 利用散列函数 获得应该将这个用户放在哪个链表上
		linLists[linListNum].tailInsert(new Node(user)); // 将该用户加入到指定的链表下
	}
	
	/**
	 * 删除用户的方法
	 * @param id
	 */
	public void delete(User user) {
		int linListNum = hashFun(user.getId()); // 通过id判断该用户应该在哪个链表出现
		linLists[linListNum].del(new Node(user)); // 进行删除操作
	}
	
	/**
	 * 修改用户的方法
	 * @param user
	 */
	public void update(User user) {
		int linListNum = hashFun(user.getId()); // 通过id判断该用户应该在哪个链表出现
		linLists[linListNum].update(new Node(user)); // 进行修改操作
	}
	
	/**
	 * 查询用户的方法
	 * @param user
	 * @return
	 */
	public User select(User user) {
		int linListNum = hashFun(user.getId()); // 通过id判断该用户应该在哪个链表出现
		Node node = linLists[linListNum].select(new Node(user));
		if(node == null) return null;
		return node.data;
	}
	
	/**
	 * 打印哈希表(所有的链表)
	 */
	public void show() {
		for (int i = 0; i < size; i++) {
			System.out.print("链表" + (i + 1) + ":"); linLists[i].show();
			System.out.println();
		}
	}
	
	/**
	 * 散列函数,通过取模法
	 * @param id
	 * @return
	 */
	public int hashFun(int id) {
		return id % size;
	}
	
}

测试类Test的创建

package edu.hebeu;

import java.util.Random;
import java.util.Scanner;

import edu.hebeu.hashtable.MyHashTable;
import edu.hebeu.hashtable.User;

public class Test {
	
	private static Scanner SCANNER = new Scanner(System.in);
	private static MyHashTable HASH_TABLE = new MyHashTable(9); // 创建包含6条链表的哈希表
	
	public static void main(String[] args) {
		menu();
	}
	
	private static void menu() {
		int id;
		String name;
		User user;
		boolean isContinue = true;
		while (isContinue) {
			System.out.println();
			System.out.println("添加用户a(add)");
			System.out.println("删除用户d(delete)");
			System.out.println("修改用户u(update)");
			System.out.println("查询用户s(select)");
			System.out.println("展示用户sa(showAll)");
			System.out.println("插入10条用户at(addTen)");
			System.out.println("退出e(exit)");
			System.out.println("请选择:"); String keyword = SCANNER.next();
			switch (keyword) {
				case "a":
					System.out.println("请输入添加用户的id:"); id = SCANNER.nextInt();
					System.out.println("请输入添加用户的name:"); name = SCANNER.next();
					user = new User();
					user.setId(id);
					user.setName(name);
					HASH_TABLE.add(user);
					break;
				case "d":
					System.out.println("请输入删除的用户id:"); id = SCANNER.nextInt();
					user = new User();
					user.setId(id);
					HASH_TABLE.delete(user);
					break;
				case "u":
					System.out.println("请输入修改的用户的id:"); id = SCANNER.nextInt();
					System.out.println("请输入修改的用户的name:"); name = SCANNER.next();
					user = new User();
					user.setId(id);
					user.setName(name);
					HASH_TABLE.update(user);
					break;
				case "s":
					System.out.println("请输入查询用户的id:"); id = SCANNER.nextInt();
					user = new User();
					user.setId(id);
					System.out.println(HASH_TABLE.select(user) == null ? "未找到" + id + "的用户" : HASH_TABLE.select(user));
					break;
				case "sa":
					HASH_TABLE.show();
					break;
				case "at":
					for (int i = 0; i < 10; i++) {
						user = new User();
						id = new Random().nextInt(1000);
						name = "user" + id;
						user.setId(id);
						user.setName(name);
						HASH_TABLE.add(user);
					}
					System.out.println("插入10条数据成功");
					break;
				case "e":
					isContinue = false;
					break;
				default:
					break;
			}
		}
	}
}

测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值