(四十)用自定义作为HashMap或HashTable的key需要注意哪些问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiangshangchunjiezi/article/details/88089192

一、HashMap=数组+链表

数组存储区间是连续的,占用内存严重,故空间复杂度很大。但数组的查找很快,为O(1)

       特点:寻址容易,插入和删除困难

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但是时间复杂度很大O(n)

      特点:寻址困难,插入和删除容易

HashMap:就是将两者综合,做一种寻址容易,插入、删除也容易的数据结构

HashMap用的是链地址法来处理冲突的,可以理解为“链表的数组”

 public static void main(String[] args) {
        Map map = new HashMap();
        map.put("What", "chenyz");
        map.put("You", "chenyz");
        map.put("Don't", "chenyz");
        map.put("Know", "chenyz");
        map.put("About", "chenyz");
        map.put("Geo", "chenyz");
        map.put("APIs", "chenyz");
        map.put("Can't", "chenyz");
        map.put("Hurt", "chenyz");
        map.put("you", "chenyz");
        map.put("google", "chenyz");
        map.put("map", "chenyz");
        map.put("hello", "chenyz");
    }

当我们新添加一个元素时,首先我们通过Hash算法计算出这个元素的Hash值的hashcode,通过这个hashcode的值,我们就可以计算出这个新元素应该存放在这个hash表的哪个格子里面,如果这个格子中已经存在元素,那么就把新的元素加入到已经存在格子元素的链表中。

运行上面的程序,我们对HashMap源码进行一点修改,打印出每个key对象的hash值

What-->hash值:8
You-->hash值:3
Don't-->hash值:7
Know-->hash值:13
About-->hash值:11
Geo-->hash值:12
APIs-->hash值:1
Can't-->hash值:7
Hurt-->hash值:1
you-->hash值:10
google-->hash值:3
map-->hash值:8
hello-->hash值:0

计算出来的Hash值分别代表该key应该存放在Hash表中对应数字的格子中,如果该格子已经有元素存在,那么该key就以链表的方式依次放入格子中

二、HashMap的创建

hashMap():构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。

装载因子:当实际长度与数组长度比为0.75时,数组长度会翻倍。

三、HashMap的插入

 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);  //解释(1)
        int hash = hash(key);  //解释(2)
        int i = indexFor(hash, table.length);  //解释(3)
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }//解释(4)

        modCount++;
        addEntry(hash, key, value, i);  //解释(5)
        return null;
    }

(1):在HashMap中,我们可以往其中插入为null的key,判断key是否为空,如果为空,则调用putForNullKey函数,putForNullKey的功能为是,查找原来HashMap中key为null的项,如果存在,则用当前的value,替换原来的value,并返回原来的value。如果不存在,则插入。之所以要单独处理,是因为如果不为null,我们需要对key进行hash映射,计算key的hash值,找到当前key在HashMap中的位置,而key为null的时候,直接默认key的hash值为0。 
(2):根据某种hash算法,计算key的hash值。 
(3):根据key的hash值与hash表的长度,计算当前这个key在hash表中的索引。 
(4):这个for循环的功能是:在原来的HashMap中,查找是否存在我现在正要插入的这个key,如果存在,则把此key对应的原来的value用当前这个value替换,并返回旧的value。值得注意的是,从for循环中可以发现,HashMap虽然是基于数组的,但同时也是基于链表的,因为我们知道,Hash表有可能会起冲突,即key的hash值一样,如果我们仅采用数组,那么当hash值一样时,在一个数组元素中,我们不可能存储两个值,因此,HashMap不仅基于数组,也基于链表,即先通过key的hash值,找到key在数组中的位置,然后每个hash相同的key又采用链表存储,因此,我们在用key的hash值定位到数组的位置时,仍然还要使用for循环来查找链表里是否真的存在当前这个key,从for中可以看到,e=e.next,就是找下一个节点。 
(5):前面的几个相当于,都是在做前期准备工作,判断key不是null,也确定当前这个key在HashMap中没有存储,接下来就是往这个HashMap插入这个键/值对。
 

Java面试笔试宝典这这样说的:

在向HashMap中添加键值对<key,value>时,需要经过如下几个步骤:

首先,调用key的hashCode()方法产生一个hash值h1

          若这个h1在HashMap中不存在,则直接将<key,value>添加到HashMap中

          若h1已存在,那么就要找出HashMap中所有hash值为h1的key(横向)

                      调用key的equals()方法判断当前添加的key值存在(true),则将新value覆盖掉旧的value值

                      调用key的equals()方法判断当前添加的key不存在(false),说明新增加的key在HashMap中不存在,因此会在

                                   HashMap中创建新的映射关系。

   举例一

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Iterator;
public class Student {
	
    public static void main(String []args) {
	 
		
      HashMap<String,String> hm=new HashMap<String,String>();
	  hm.put("k1","v1");
	  hm.put("k1","newv1");
	  Iterator<Entry<String,String>> it=hm.entrySet().iterator();
	  while(it.hasNext())
	  {
		  Entry<String,String> e=it.next();
		  System.out.println(e.getKey()+"----"+e.getValue());
	  }
	
    }
}
//结果: k1----newv1

解释:<k1,v1>存入,<k1,newv1>存入时,先根据key的hashCode()生成hash,存在,则横向遍历,k1.equal(k1)==true【比较的是两个字符串,String重写了equals()】,新值替换旧值。所以hash表size==1

举例二:

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Iterator;
public class Student {
	String name;
	String id;
	public Student(String  name,String id)
	{
		this.name=name;
		this.id=id;
	}
	public String toString()
	{
		return "id="+id+"---"+"name="+name;
	}
    public static void main(String []args) {
	   HashMap<Student,String> hm=new HashMap<Student,String>();
	  hm.put(new Student("Spig","001"),"sp1");
	  hm.put(new Student("Spig","001"),"sp2");
      Iterator<Entry<Student,String>> it=hm.entrySet().iterator();
	  while(it.hasNext())
	  {
		  Entry<Student,String> e=it.next();
		  System.out.println(e.getKey()+"----"+e.getValue());
	  }
	
    }
结果:id=001---name=Spig----sp1
     id=001---name=Spig----sp2

解释:<new Student("Spig","001"),"sp1">存入,<new Student("Spig","001"),"sp2">存入时,先根据key的hashCode()生成hash,不存在,存入新位置。所以hash表size==2

因此,若要根据自己的逻辑判定是否为同一个key,则要重写hashCode()和equal方法

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Iterator;
public class Student {
	String name;
	String id;
	public Student(String  name,String id)
	{
		this.name=name;
		this.id=id;
	}
	public String toString()
	{
		return "id="+id+"---"+"name="+name;
	}
	public int hashCode(){
		System.out.println(id.hashCode());
		return id.hashCode();
	}
	public boolean equals(Object obj){
		
		Student s=(Student)obj;
		if(s.id.equals(this.id))
		
			return true;
		
		else
			return false;
	}
    public static void main(String []args) {
	   HashMap<Student,String> hm=new HashMap<Student,String>();
	  hm.put(new Student("Spig","001"),"sp1");
	  hm.put(new Student("Spig","001"),"sp2");
      Iterator<Entry<Student,String>> it=hm.entrySet().iterator();
	  while(it.hasNext())
	  {
		  Entry<Student,String> e=it.next();
		  System.out.println(e.getKey()+"----"+e.getValue());
	  }
	
    }
结果:47665
47665
id=001---name=Spig----sp2

HashSet:元素唯一性hashCode、equals,元素唯一性执行过程解释:https://blog.csdn.net/jiangshangchunjiezi/article/details/75212185

参考:https://blog.csdn.net/strongyoung88/article/details/47429559

          http://www.blogjava.net/dongbule/archive/2011/02/15/344387.html

猜你喜欢

转载自blog.csdn.net/jiangshangchunjiezi/article/details/88089192