hashmap是单向链表吗_HashMap的实现原理--链表散列

1.    HashMap概述

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

2.    HashMap的数据结构

在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。这样的结构结合了链表在增删方面的高效和数组在寻址上的优势

hashmap结构:哈希表是由数组+链表组成的,数组的默认长度为16 ,注意 hashtable的数组的默认长度是11(可以自动变长。在构造HashMap的时候也可以指定一个长度),数组里每个元素存储的是一个链表的头结点。而组成链表的结点其实就是hashmap内部定义的一个类:Entity。Entity包含三个元素:key,value和指向下一个Entity的next

3.  HashMap的存取

这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%(len-1)获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

HashMap的存储--put:

int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值

int index = hash %( Entry[].length-1);

table[index] = value;//假定存储链表头结点的数组名为table

用table[index]表示通过hash值计算出来的、元素需要存储在数组中的位置。先判断该位置上有没有存有Entity,没有的话就创建一个Entity对象,在该位置上插入,插入结束;如果有的话,通过链表的遍历方式去逐个遍历,通过equals方法将key和已有的key进行比较,看看有没有已经存在的key,有的话用新的value替换老的value;如果没有,则在table[index]插入该Entity,把原来在table[index]位置上的Entity赋值给新的 Entity的next,也即,新的Entity插入(put)的位置永远是在链表的最前面(百度面试),这样插入结束。

打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:table[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,table[0] = B,如果又进来C,index也等于0,那么C.next = B,table[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。

注:null key总是存放在Entry[]数组的第一个元素。

扩展:

按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同。

如果两个不同对象的hashcode相同,就称为冲突。冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。覆盖了equals方法之后一定要覆盖hashCode方法,原因很简单,比如,String a = new String(“abc”);String b = new String(“abc”);如果不覆盖hashCode的话,那么a和b的hashCode就会不同,把这两个类当做key存到HashMap中的话就 会出现问题,就会和key的唯一性相矛盾。

HashMap的读取--get:

先定位到数组元素,再遍历该元素处的链表

publicV get(Object key) {if (key == null)returngetForNullKey();int hash =hash(key.hashCode());//先定位到数组元素,再遍历该元素处的链表

for (Entry e =table[indexFor(hash, table.length)];

e!= null;

e=e.next) {

Object k;if (e.hash == hash && ((k = e.key) == key ||key.equals(k)))return e.value;//显然,在寻找目标元素的时候,除了对比通过key计算出来的hash值,还会用双等或equals方法对key本身来进行比较,两者都为true时才会返回这个元素

}return null;

}

遍历规则:如下:

importjava.util.Collection;importjava.util.HashMap;importjava.util.Iterator;importjava.util.Map;importjava.util.Map.Entry;public classabc_test {public static voidmain(String[] args) {//TODO Auto-generated method stub

Map map=new HashMap();

map.put("1", "value1");

map.put("2", "value2");

map.put("3", "value3");

map.put("4", "value4");//第一种:普通使用,二次取值

System.out.println("\n通过Map.keySet遍历key和value:");for(String key:map.keySet())

{

System.out.println("Key: "+key+" Value: "+map.get(key));

}//第二种:推荐,尤其是容量大时

System.out.println("\n通过Map.entrySet遍历key和value");for(Map.Entryentry: map.entrySet())

{

System.out.println("Key: "+ entry.getKey()+ " Value: "+entry.getValue());

}//第三种

System.out.println("\n通过Map.values()遍历所有的value,但不能遍历key");for(String v:map.values())

{

System.out.println("The value is "+v);

}//第四种

System.out.println("\n通过Iterator 遍历Map.entrySet的key和value: ");

Iterator map1it=map.entrySet().iterator();while(map1it.hasNext())

{

Map.Entry entry=(Entry) map1it.next();

System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());

}//第五种

System.out.println("\n通过iterator 遍历Map.keySet的key和value: ");

Iterator keyIter=map.keySet().iterator();while(keyIter.hasNext())

{

String key=(String)keyIter.next();

System.out.println("Key: "+key+" Value: "+map.get(key));

}

}private static void print(Collectionset)

{

Iterator it =set.iterator();while(it.hasNext())

{

Person p=it.next();

System.out.println(p.toString());

}

}

}classtest {public intd;publicString name;public intgetD() {returnd;

}public void setD(intd) {this.d =d;

}publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}

@Overridepublic booleanequals(Object obj) {//TODO Auto-generated method stub

return super.equals(obj);

}

}classPerson

{public Person(String name, intage)

{this.name =name;this.age =age;

}privateString name;private intage;publicString getName()

{returnname;

}public voidsetName(String name)

{this.name =name;

}public intgetAge()

{returnage;

}public void setAge(intage)

{this.age =age;

}publicString toString()

{return "{" + name + ", " + age + "}";

}

}class joinDemo implementsRunnable{

@Overridepublic voidrun() {//TODO Auto-generated method stub

for(int i=0;i<10;i++){

System.out.println("线程1第"+i+"次执行");

}

}

}

结果为:

通过Map.keySet遍历key和value:

Key:1Value: value1

Key:2Value: value2

Key:3Value: value3

Key:4Value: value4

通过Map.entrySet遍历key和value

Key:1Value: value1

Key:2Value: value2

Key:3Value: value3

Key:4Value: value4

通过Map.values()遍历所有的value,但不能遍历key

The value is value1

The value is value2

The value is value3

The value is value4

通过Iterator 遍历Map.entrySet的key和value:

Key:1Value: value1

Key:2Value: value2

Key:3Value: value3

Key:4Value: value4

通过iterator 遍历Map.keySet的key和value:

Key:1Value: value1

Key:2Value: value2

Key:3Value: value3

Key:4 Value: value4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值