`
十三月的
  • 浏览: 168820 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

手写简单版HashMap

 
阅读更多

        手写实现基本功能的HashMap

        1)属性:

    • 内部节点类                         Node 
    • 存放Node类型数据的数组     hashTable
    • 数组的容量                         capacity
    • 当前存放数据的数量             count
    • 能够存放的数据数量的上限    threshold
    • 装载因子                            loadFactor

       

        2)

          功能:

    • 添加 put(K key,V value);
    • 删除 delete(K key);
    • 查找 getV(K key);

       3)理解:

    • 能够添加的数据上限:threshold=capacity* loadFactor;
    • 默认的loadFactor是2.0f;
    • 默认的容量是capacity=100;
    • put 判断key是否已经存在的标准解释:由于使用泛型不知道用户的key对象的类型,故使用的是用户对象的equals判别方法,形式:key.equals(已经存在的key),添加和删除同样是使用了该判别方法
    • 测试使用的key为User类   
    • User类 重写了Object的hashCode() 方法和equals() 方法
    • hashCode(): 以用户的年龄(Int)表示
    • equals()  为了方便测试,没有采用Object的指向同意对象的“==”方法,而是根据User 的hashCode 即年龄为标准,年龄相等即表示是equals 返回true
    • 系统提供的HashMap 是先添加用户,再判断容量是否超限,自己写的是先判断是否超限再添加,因此在临界数量上如在添加第201个用户时,count判断超限,进行了resize,之后还要经该用户添加到新的hashTable中,防止遗漏

     

    代码:User类

    public class User {
    
    	private String name;
    	private int sex;
    	private int age;
    	private int phone;
    	
    
    	
    	public String getName() {
    		return name;
    	}
    	public int getSex() {
    		return sex;
    	}
    	public int getAge() {
    		return age;
    	}
    	public int getPhone() {
    		return phone;
    	}
    	
    	public void setName(String name) {
    		this.name = name;
    	}
    	public void setSex(int sex) {
    		this.sex = sex;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public void setPhone(int phone) {
    		this.phone = phone;
    	}
    	
    	//重写haseCode
    	public int hashCode(){
    		return this.age;
    	}
    	
    	//重写equals
    	public boolean equals(Object o){
    		if(o instanceof User){
    			User u=(User)o;
    			//此处只是简单的进行了判断  以年龄为标准
    			if(u.getAge()==this.age){
    				return true;
    			}
    		}
    		return false;
    	}
    }

     

     

    MyHashMap:

    import java.util.Random;
    
    
    public class MyHashMap<K,V> {
    
    	/**
    	 * 定义自己的哈希表 测试效率问题
    	 * @param args
    	 */
    	
    	
    	/**
    	 * 定义一个内部类
    	 * @param args
    	 */
    	
    	
    	public class Node<K,V>{
    		
    		private int keyCode;
    		private K key;
    		private V value;
    		private Node<K,V> next;
    		
    		/**
    		 * 构造函数
    		 * @param k
    		 * @param v
    		 * @param next 在创建新的节点的时候 直接指出下一个节点
    		 */
    		public Node(int keyCode,K key,V value,Node<K,V> next){
    			this.keyCode=keyCode;
    			this.key=key;
    			this.value=value;
    			this.next=next;
    		}
    		
    		
    	}
    	
    	static int DEFAULT_CAPCITY=100;
    	//内部数组 ----存放的是Node类型的链表
    	private int capacity;
    	private Node[] hashTable;
    	//定义所有的key-value对的总数
    	private int count=0;
    	//装载因子
    	private float loaderFactor;
    	//count的上限
    	private int threshold;
    	
    	/**
    	 * 构造函数
    	 */
    	public MyHashMap(){
    		//初始化容量
    		capacity=DEFAULT_CAPCITY;
    		//初始化hashTable
    		hashTable=new Node[capacity];
    		//初始化装载因子
    		loaderFactor=2.0f;
    		//初始化count的上限
    		threshold=(int) (capacity*loaderFactor);
    	}
    	
    	
    	/**
    	 * 添加
    	 * @param k
    	 * @param v
    	 * @return
    	 */
    	
    	public V put(K key,V value){
    		//首先是根据K 计算出hash
    		int keyCode=key.hashCode();
    		int hash=getHash(keyCode);//此处重写K如User的hashCode 
    		//判断该hash下是否已经存在该索引  并作出抉择
    		//System.out.println("put----1 -----:Hash ="+hash);
    		for(Node<K,V> n=hashTable[hash];n!=null;n=n.next){
    			
    			if(key.equals(n.key)){//如果已经存在该索引 则返回oldValue
    				//将就得value改成新的value 索引不用改变
    				//以输入对象的equals 判断标准判读
    				V oldValue=n.value;
    				n.value=value;
    				return oldValue;
    			}
    		}
    		//索引并没有存在 
    		addNode(key,value,hash);
    		return null;
    	}
    	
    	/**
    	 * 查找
    	 * @param k
    	 * @return
    	 */
    	public V getV(K key){
    		//根据输入的Key 计算出hash
    		int hash=getHash(key.hashCode());
    		//从header遍历
    		for(Node<K,V> node=hashTable[hash];node!=null;node=node.next){
    			if(key.equals(node.key)){//此处采用的是用来查找的key 自身的equals方法  所以需要重写equals()
    				//其实此处最好是这样 泛型的K key只能是使用Object 的== 判断  (指向)
    				return node.value;
    			}
    		}
    		//如果没有该key  返回null
    		return null;
    	}
    	/**
    	 * 删除
    	 * @param k
    	 * @return
    	 */
    	public boolean delete(K key){
    		
    		//首先是查找是否存在该key
    		int hash=getHash(key.hashCode());
    	
    		//存放上一个Node 
    		Node<K,V> pre=null;
    		Node<K,V> n=hashTable[hash];
    		
    		for( ;n!=null;n=n.next){
    			if(key.equals(n.key)){
    				//删除
    				if(pre==null){//删除header
    					hashTable[hash]=n.next;
    					return true;
    				}else{
    					pre.next=n.next;
    					n=null;
    					return true;
    				}
    			}
    			pre=n;
    		}
    		
    		return false;
    	}
    	
    	
    	/**
    	 * 计算hash值
    	 * @param num 输入的数值 相当于key的hashCode
    	 * @return
    	 */
    	private int getHash(int num){
    		return num%capacity;
    	}
    	
    	/**
    	 * 添加节点
    	 * @param k
    	 * @param v
    	 * @param bucketIndex 指明添加的索引
    	 * @return
    	 */
    	private void addNode(K key,V value,int bucketIndex){
    	    
    		if(count<threshold){//小于上限
    		//取得链表的head
    		Node<K,V> n=hashTable[bucketIndex];
    		//改变链表的head
    		int keyCode=key.hashCode();
    		hashTable[bucketIndex]=new Node<K,V>(keyCode,key,value,n);
    		count++;
    		//System.out.println("addNode---2----  此时的count="+count);
    		}else{//大于上限
    		//重新建表
    		resize();
    		//系统的map是不管是否超限  先添加上再判断 此处要先判断的话 就要在重新建表后  将该用户添加上
    		//这种问题总是出现在衔接的地方  
    		//取得链表的head
    		Node<K,V> n=hashTable[bucketIndex];
    		hashTable[bucketIndex]=new Node<K,V>(key.hashCode(),key,value,n);
    		count++;
    	}
    		
    	}
    	/**
    	 * 处理冲突 重新建表
    	 */
    	private void resize(){
    	//	System.out.println("需要重新建表....count="+count);
    		//容量增加一倍  capacity 增加到200 或者其他  有个好处不用将所有的用户重新存放
    		capacity=capacity*2;
    		//新建
    		Node [] newHashTable=new Node[capacity];
    		//遍历
    		for(int i=0;i<hashTable.length;i++ ){
    			
    			
    			Node<K,V> oldNode=hashTable[i];
    			while(oldNode!=null){
    			//准备工作---依次取出
    			Node<K,V> next=oldNode.next;
    			
    			//改变旧的地盘
    			hashTable[i]=next;
    			//安排新的地盘
    			int newHash=getHash(oldNode.keyCode);
    			Node<K,V> newNext=newHashTable[newHash];
    			oldNode.next=newNext;
    			newHashTable[newHash]=oldNode;
    			
    			//重新指向
    			oldNode=hashTable[i];
    			}
    			
    		}
    		//循环过后 table 已经全部是指向了null
    		hashTable=newHashTable;
    		//改变上限 在这出错
    		threshold=(int) (capacity*loaderFactor);
    		
    	}
    	}

     

     简单测试一把,按照顺序插入100万用户,是3200毫秒,系统的是2100。查找时间数量大系统的11毫秒,自己测试17毫秒。 使用了随机数,数据不一样,查看了系统的在计算hash值的时候不是采用取余,而是按位&,结果一样但是效率高,当然系统采用的默认容量是16,之后容量扩充时是2倍和默认装载因子0.75,是有其他考虑......

     

     

    分享到:
    评论

    相关推荐

      Java手写简易版HashMap的使用(存储+查找)

      Java手写简易版HashMap的使用(存储+查找) Java手写简易版HashMap是Java中的一种常用的数据结构,它提供了高效的存储和查找键值对的功能。下面我们将详细介绍Java手写简易版HashMap的使用,包括存储和查找键值对的...

      手写简易版SpringMVC,探索SpringMVC原理

      在本文中,我们将深入探讨SpringMVC的原理,并尝试通过手写一个简易版来理解其核心机制。 首先,SpringMVC的工作流程如下: 1. 用户发送请求:客户端通过HTTP协议向服务器端发送请求,请求中包含了URL和方法类型...

      HashBuck.txt

      手写简单的HashMap

      ConcurrentHashMap:今天在看Spring源码的时候发现了一个并发安全的hashmap,自己就手写实现了一下

      现在,我们尝试手写实现一个简单的并发安全哈希映射: ```java import java.util.*; import java.util.concurrent.locks.*; public class MyConcurrentHashMap, V&gt; { private static final int DEFAULT_INITIAL_...

      Android 手写EventBus,ButterKnife=动态代理+注解+反射

      现在,我们来手写一个简单的EventBus实现: 1. 定义一个事件基类,用于标记所有的事件: ```java public class Event { // ... } ``` 2. 创建一个`EventBus`类,包含订阅者注册、注销和发送事件的方法: ```java ...

      22_redis的过期策略能介绍一下?要不你再手写一个LRU?.zip

      为了模拟LRU算法,你可以编写一个简单的Java程序,使用LinkedList和HashMap作为基础数据结构,实现插入、访问和淘汰操作。在实际编程中,可以考虑使用Java的LinkedHashMap,它内置了LRU特性,能简化实现。 总结来说...

      最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

      面试题包含了不同技术层面的面试问题,同时也能对一些没有面试开发经验的小白给予不可估量的包装, 让你的薪水绝对翻倍, 本人亲试有效.Java面试题84集、java面试专属及面试必问课程...└─面试必问-聊聊哈希算法与HashMap

      http_server8.zip

      ArrayList、LinkedList、HashMap、HashSet等都是Java集合类库中的常见组件,它们在处理数据存储和操作中发挥着关键作用。在构建服务器时,你可能需要使用这些集合来存储请求信息、管理连接池或者缓存数据。理解它们...

      经典sql-java面试题

      6. **存储过程和触发器**:理解这两者的概念和应用场景,能编写简单的存储过程和触发器。 接下来是Java部分。Java是一种广泛应用的面向对象的编程语言,广泛应用于Web开发、移动应用、企业级应用等。面试中可能涉及...

      Java后端技术面试汇总-副本.pdf

      涉及到的自定义数据结构,如简单的HashMap的实现,以及ThreadLocal原理的分析也是这一部分的重点。 3、进程和线程 在这一部分中,详细讨论了进程和线程的概念,包括它们之间的区别以及并行和并发的区别。探讨了多种...

      source-learning:原始学习

      手写简单HashMap的过程可以帮助我们理解其内部的工作原理,包括散列函数的设计、数组与链表结合的动态扩容策略等。散列函数用于将键转换为数组索引,良好的散列函数可以减少冲突,提高查找效率。链表则用于解决冲突...

      Java后端技术面试汇总-2019

      - **手写简单的HashMap**:实现基本的put和get方法,了解哈希函数和链表的使用。 - **看过那些Java集合类的源码**:例如ArrayList、HashMap等。 **1.3 进程和线程** - **线程和进程的概念、并行和并发的概念**: ...

      JSONObject详解及用法.pdf

      - 编写难度:XML的结构清晰,便于手写,但JSON的语法更简洁,所需的字符较少。 - 体积:JSON通常比XML更紧凑,特别是在处理大量数据时。 - 解析速度:JSON解析通常比XML更快,因为其结构更简单。 4. 使用JSON库...

      简单的JavaExcel进程sep4j.zip

      你不必手写任何 POI 相关代码。支持 Maven. 基本示例把数据写入Excel Collection users = Arrays.asList(user1, user2);  LinkedHashMap, String&gt; headerMap = new LinkedHashMap, String&gt;();  ...

      大公司的Java面试题集

      对于常见的排序和查找算法,如冒泡排序、快速排序、二分查找等,面试者应能够手写代码实现。 7. **网络编程**:理解TCP/IP协议,包括三次握手、四次挥手,以及HTTP和HTTPS的工作原理。熟悉Socket编程,能够编写简单...

      2020最强Java面试题共(6000页).zip

      - ORM框架:了解Hibernate、MyBatis等ORM框架的工作原理,减少手写SQL的繁琐。 9. **框架应用** - Spring框架:掌握依赖注入(DI)和面向切面编程(AOP),理解Spring Boot的自动配置。 - Spring MVC:理解Web...

      小码农的代码(一)----------SpringJDBC的使用

      首先,SpringJDBC提供了一个抽象层,它将传统的JDBC API封装起来,减少了手写模板代码的需求,使得数据库操作更加简洁、易读。通过使用Spring的JdbcTemplate和NamedParameterJdbcTemplate,开发者可以避免处理连接...

      javascript 实现map集合

      文章通过具体的实现代码,向我们展示了一个简单版本的Map集合。 首先,文中定义了一个名为`varMap`的函数,它利用了一个对象`hashmap`和几个数组来维护键、值和条目。`hashmap`对象用于存储键和对应的值,`keys`...

      达内Java技术之高频面试题3.0(1).docx

      1. **冒泡排序(Bubble Sort)**:冒泡排序是一种简单的排序算法,通过重复遍历待排序的数列,依次比较相邻元素,若顺序错误则交换,直到没有更多的交换发生,表明已排序完成。 2. **快速排序(Quick Sort)**:...

    Global site tag (gtag.js) - Google Analytics