hashmap源码学习整理

来源:互联网 发布:高仿香港身份证淘宝 编辑:程序博客网 时间:2024/05/22 09:38

首先从put方法说起(可能有点跳跃),而要说到put方法,就不得不说一下java中判断2个对象相等的方式(因为put方法里要用到),说到这个,那就自然牵扯到2个联系很密切的方法hashcode(),equals()。

这2个方法是在Object类中定义的,我们可以看下官方jdk对它们的定义(一部分):

If two objects are equal according to the {@code equals(Object)} method, then calling the {@code hashCode} method on each of the two objects must produce the same integer result.

It is not required that if two objects are unequal according to the{@link java.lang.Object#equals(java.lang.Object)}method, then calling the {@code hashCode} method on each of the two objects must produce distinct integer results.  However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.


第一段:如果2个对象equals为true,那么他们的hashcode必须返回同样的值。

第二段:如果2个对象equals为false,那么他们的hashcode也可以返回同样的值。

还有这句话:However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

对于不equal的对象返回不同的hashcode值会提高"某些集合类"的性能(就是这些在放入对象到集合时需要判断对象是否相等的集合)

为什么呢?

想象一下,放入1000个对象到hashmap中,如果没有hashcode方法,那么就需要调用1000次equals方法,但是有了hashcode方法,我们可以先判断hashcode相不相等,不相等的话就直接可以判定这2个对象不相等,而这个的前提是建立在你对这个对象的这两个方法的定义:你确保了不相等的对象一定返回不同的hashcode;相等再去调用equals方法比较。(equals方法的性能开销是绝对大于hashcode的)

我们可以看下源码(比较的这一段):

if (p.hash == hash &&                ((k = p.key) == key || (key != null && key.equals(k))))

可以看到如果hash值不一样,就不会去计算&& 后面的这个判定方法(即equals方法)

我们可以用代码去验证一下:

class Country {String name;long population;public Country(String name, long population) {this.name = name;this.population = population;}public String getName() {return name;}public void setName(String name) {this.name = name;}public long getPopulation() {return population;}public void setPopulation(long population) {this.population = population;}@Overridepublic int hashCode() {if (this.name.length() % 2 == 0)return 390;elsereturn 950;}@Overridepublic boolean equals(Object obj) {return true;}}public class HashMapStructure {public static void main(String[] args) {Country india = new Country("india", 1000);Country japan = new Country("japan", 10000);System.out.println(india.hashCode());Country france = new Country("france", 2000);Country russia = new Country("russia", 20000);System.out.println(france.hashCode());HashMap<Country, String> countryCapitalMap = new HashMap<Country, String>();countryCapitalMap.put(russia, "Moscow");countryCapitalMap.put(india, "Delhi");countryCapitalMap.put(japan, "Tokyo");countryCapitalMap.put(france, "Paris");System.out.println(countryCapitalMap.size());Iterator<Country> countryCapitalIter = countryCapitalMap.keySet().iterator();while (countryCapitalIter.hasNext()) {Country countryObj = countryCapitalIter.next();String capital = countryCapitalMap.get(countryObj);System.out.println(countryObj.getName() + "----" + capital);}}}

定义了一个country类,name和population2个属性,定义了一个hashmap存放country以及其首都名字。
注意country类的equals和hashcode方法,如果country名字是单数返回同样的hashcode,双数返回另一个hashcode,而equals我直接返回true,这意味着所有country对象调用这个函数都比较true;
这意味着当这样前后放入:
countryCapitalMap.put(india, "Delhi");countryCapitalMap.put(japan, "Tokyo");
第一个(india)hash到对应地址,填入,第二个(japan)hash到对应地址,显然japan hash计算后返回与india相同的值,产生冲突,所以接下来要判断key是否相等(如果相等,覆盖value,不相等,则就是所谓的哈希冲突,java的解决办法是链地址法,即用一个链表将hash值相等的对象串起来。所以hash函数最后设计的尽量分布,避免产生冲突),显然hashcode相等,再比较equals,当然也是返回true,所以此时覆盖value,india的首都就变成了tokoy。
这里其实也可以看出来,如果2个对象equals返回true,但是hashcode不相等,那么这2个对象在计算hash地址时一定不会冲突,那么都可以添加到map中,但是其实2个对象是相等的,那么就违反了hashmap的规则。
所以最终程序输出:

2

russia----Paris

india----Tokyo

put方法就讲到这里。


to be continued..


0 0
原创粉丝点击