Java HashMap深度解析-面试问题全面解读(1)

本文深入探讨了HashMap的工作原理,包括其内部结构、hashcode与equals方法的重要性及其实现细节。通过具体示例帮助读者理解如何正确地重写这些方法,以避免常见的错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

           最近在做java的面试工作,发现很多童鞋在跪在了HashMap题目上面,觉得很不应该。另外在优快云上经常看到HashMap解读文章,看了文章以后学了很多东西,但总有一种不是很痛快的感觉,于是决定写一篇文章,来解读一下HashMap,并顺便讲解一下HashMap常见面试题。
          首先附上公司的面试题,大家先来试试:HashMap特性性是什么?常用的使用场景是什么?HashMap的默认长度是多少?创建一个空的Map占用多少内存?

     问题1:hashCode()和equals(Object obj)方法在Hash这种数据结构中的作用是什么?

               如果你能回答这问题,HashMap就掌握的差不多了,现实情况是,大多数面试者回答不上来。所以在回答这个问题之前.我们先来回顾一下equals方法和hashCode方法的作用:
              equals和hashCode方法是Object类方法,在Object源代码中equals方法是这样定义的:
     public boolean equals(Object obj) {
	     return (this == obj);
     }

      从方法定义我们可以得出结论,默认的equals是比较2个对象的引用是否相等,如果要比较2个对象的值是否相等,需要我们重写equals方法。在equals方法定义上面还有这么一段说明:

           Indicates whether some other object is "equal to" this one.The equals method implements an equivalence relation on non-null object references.这段英文的意思是说:equals方法是用来判断方法参数中的(Object obj)和当前对象(this)是否相等,方法的实现者在和非空的对象比较时具有对等性,至于对等性怎么理解大家可以去查看Object类中的定义,但Java语言对equals()方法重写的要求如下:

            对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
            反射性:x.equals(x)必须返回是“true”。
            类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
            一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
           任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。

            2) hashcode() 方法,在object类中定义如下:             
 public native int hashCode();
     说明是一个本地方法,它的实现是根据本地机器相关的  。 在方法上面也有这么一段说明:Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.As much as is reasonably practical, the hashCode method defined by  class Object does return distinct integers for distinct  objects这段英文非常简单,但表达了一个重要概念:hash code是给 hashtables这样的hash算法的数据接口用的(用处是什么,后面我们会讲),  Object的hashCode方法对不同的对象返回不同的hash code。
            在equals方法上的还有一段英文:
          Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes. 这段英文的意思是说:当我们重写equals一般情况下需要重写hashCode方法,并且保证,2个对象如果equals为true,hash code值一定相等。
         分析至此,我们可以得到如下结论:
           1)Java语言对equals()和hashcode方法重写的要求:如果2个对象equals相等,hash code值相等。反之则不一定成立。

         2)hash code值只有在使用hash算法的数据结构时,才会有用,所以对象不会放到hash算法数据结构时,只重写equals方法或hash code不相等,也不会出问题(按照规范一样要写)

           我相信,看到这儿,大家对equals()和hashcode应该会有新的理解,相关面试题应该难不倒大家,在这儿给大家建议:多学习英语,多看java源代码,源代码和里面的注释是最好的学习资料。

         下面我们开始来解析,hashcode值和equals方法在HashMap里面的使用,我们先来看看HashMap的put和get方法的源码,代码中已经做了详细注释:

       

 public V put(K key, V value) {
    	//若干key为空,就放入null对象。所以HashMap是可以NUll对象的
        if (key == null)
            return putForNullKey(value);
        //这个hash值并不是直接用的key的hash code值,而是取出hash code值,在用hash发送计算一下。
        //hash方法的目的是为了保证hash code值的唯一,防止hash冲突(hash碰撞),方法也比较简单就是把hash code值位移再^
        int hash = hash(key.hashCode());
        //indexFor,根据计算出来的的hash值和table数组长度计算出key在数组的下标,防止下标越界
        int i = indexFor(hash, table.length);
        //for循环处理冲突的,如果hash值相同,则在该位置用链表存储
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果hash 也相等,并且equals也相等,就用新值覆盖旧值
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        //把对象放入到table数组数组第i个下标,
        addEntry(hash, key, value, i);
        return null;
    }

    public V get(Object key) {
    	//如果key为空,就获取null key对应的值
        if (key == null)
            return getForNullKey();
        //计算key的hash值
        int hash = hash(key.hashCode());
        //indexFor获取key在数据中的下标
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            //判断key的hash code是否相等,并且是否equals.
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

       从源代码中我们可以得出如下结论:

        1)HashMap是一种数组+链表的结构,定义如下:

     /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
      transient Entry[] table;
        链表:
      Entry<K,V> next;

      结构如图:

     


      

         2)往HashMap放入对象时,首先根据key的hash code值算出存放在Entry[] table中的下标,再根据equals方法判断存放在链表的位置,从hashmap 获取对象时,先根据hash code值算出存放在Entry[] table中的下标,再返回再链表上equals相等对象

        下面就留一段代码给大家,大家运行一下,看看输出什么:

        

public static void main(String[] args) {
	   Map<Person,String> personMap=new HashMap<Person,String>();
		personMap.put(new Person("csdn",10),"aaa");
		personMap.put(new Person("csdn",11),"bbb");
		personMap.put(new Person("china",10),"ccc");
		//问题1:这个是size是几??
		System.out.println(personMap.size());
		String mapObject=personMap.get(new Person("csdn",10));
		//问题2:这个时候会输出设么??
		System.out.println(mapObject);
	}
	
	static class Person{
		
		private String name;
		private int age;
		
		public Person(String name,int age){
			this.name=name;
			this.age=age;
		}
		
	}


      下一篇预告:HashMap的长度管理和负载因子
           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值