HashMap学习笔记1

先说说HashMap的几个特点:

1、无序的(那它的存取速度咋还那么快呢?)

2、线程不安全的(存取不同步)

第二个特点没撒好说的,我们主要说说第一个;首先我们先看一下HashMap的结构图:


由上图我们可以看出,它是一个数组加列表的混合结构(横行数组,纵向链表)。结合结构图和源码我们一起来看看HashMap的put和get的原理:

a、put源码

public V put(K key, V value) {

if (key == null)

return putForNullKey(value); //null总是放在数组的第一个链表中

int hash = hash(key.hashCode());

int i = indexFor(hash, table.length);//数组对应的位置

//遍历链表

for (Entry e = table[i]; e != null; e = e.next) {

Object k;

//如果key在链表中已存在,则替换为新value

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(hash, key, value, i);

return null;

}

void addEntry(int hash, K key, V value, int bucketIndex) {

Entry e = table[bucketIndex];

table[bucketIndex] = new Entry(hash, key, value, e); //参数e, 是Entry.next

//如果size超过threshold,则扩充table大小。再散列

if (size++ >= threshold)

resize(2 * table.length);

}

由源码可以看出put操作分为以下几步:

1、对key值进行hash编码,得到对应的数组位置

2、取出对应位置的链表,如果链表为空,则直接插入;否则遍历链表,存在相同key,就覆盖,不存在就在链表末尾插入

对于这个两点是不是觉得有点抽象,不是很好懂,缺点什么?我们来说点人话,为什么要做上述操作呢?有个专业名词叫散列冲突拉链法,HashMap的初始化数组大小是16,在对大量数据操作hashcode很容易产生碰撞冲突,为了解决冲突就加上了链表结构。

b、get源码

public V get(Object key) {

if (key == null)

return getForNullKey();

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;

}

return null;

}

有了上面put的讲解之后,是不是觉得get就很容易理解了!还是简单写一下,它和put一样也是分为两步:

1、对key值进行hash编码,得到对应的数组位置

2、取出对应位置的链表,遍历链表找出key值对应的值

分解完HashMap的存取机制之后,我们来看看它的两种遍历方式:

第一种

Map map = new HashMap();

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()) {

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

Object key = entry.getKey();

Object val = entry.getValue();

}

效率高,以后一定要使用此种方式!

第二种

Map map = new HashMap();

Iterator iter = map.keySet().iterator();

while (iter.hasNext()) {

Object key = iter.next();

Object val = map.get(key);

}

效率低,以后尽量少使用!

骚年们按耐不住了吧,一定想问为什么吧?满足你,一切都在源码中:

private final class KeyIterator extends HashIterator {

public K next() {

return nextEntry().getKey();

}

}

private final class EntryIterator extends HashIterator> {

public Map.Entry next() {

return nextEntry();

}

}

从源码中我们可以看出

方法一:keySet只是将HashMap中所有的key值进行了iterator,然后根据key值重新去map中取值(相信看了前面的讲解,你一定明白这个重新是很耗时的)

方法二:entryset只遍历了1次,就把key和value都放到了entry中,所以就快了。

意犹未尽?再来点什么?好满足你:

从存取机制中我们看出来,所有的key值都要进行一次hash编码操作,再去定位存放位置的,所以你选定的key值对你的存取速度影响是非常大的。理论上Integer的速度应该是最快的,因为它的hash编码就是它本身,当然使用最多的String速度也还好,你非要逆天的整一些奇葩的object对象,那我也就无话可说了!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算...
    曹振华阅读 7,196评论 1 37
  • 5.1、对于HashMap需要掌握以下几点 Map的创建:HashMap() 往Map中添加键值对:即put(Ob...
    rochuan阅读 3,957评论 0 0
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 9,705评论 0 16
  • 我的前半生》中唐晶也曾沉迷失恋的打击中,是贺涵当初觉得她可成大器便格外培养,如今的唐晶成为可以拿下贺涵都搞不定的客...
    菲姐不够飞阅读 5,299评论 3 5
  • 老蔡吃海底捞,说海底捞服务有很好的良性循环。我觉得良性循环这个词用的很好,我们说世上的事物一直处在变化之中,永远没...
    wakeupme阅读 1,410评论 0 1