`

Map浅析

阅读更多

java.Util.Map接口是jdk的常用接口,Map是一种数据字典,以key,value形式出现,key、value作为数据域出现在内部接口Entry<K,V>中。提供根据key查value,以及keys和values集合并遍历的方法。没什么特殊的地方,只是对于几种Map,如果能知道其数据结构和相对优劣,对于编写性能较好的程序是需要掌握这些的。

 

1.Hashtable

jdk的遗留类。提供了线程安全的添加、删除等接口,通过内部互斥锁Synchronized实现。

 

public synchronized V put(K key, V value)
public synchronized V get(Object key)

 

 

2.HashMap

数据结构是数组,数组元素是单向链表。数据结构如下图:


 

常用方法:

put(key,value)

1)如果key为null

如果key==null,则执行putForNullKey()。HashMap只允许存在一个key为null的元素,value后值覆盖前值。

 

2)计算key的hash值

根据jdk的hash算法,计算key的hash值。

 

3)定位元素在数组中的位置

执行indexFor()方法,得到元素在数组中的index。

 

4)数组中已存在相同的key

遍历数组,判断数组中是否已经有相同的key,已经存在,则value后值覆盖前值,返回oldValue。

判断条件是两个key的hash值相同,并且两者的”==”或者“equals”比较返回true。如果key是String对象,则内容相同或者是同一个对象都判定为key相同;如果是自定义对象,则需要重写equals方法来规定判断标准。如果以自定义的对象作为key,则需要重写equals()和hashcode()。

 

5)数组中不存在该key

将元素包装为Entry类型,添加元素到数组,并将新Entry对象添加到链表头。

 

 特点:

1)可以存储null键和null值,Hashtable不允许;

两个关键参数:initial capacity和load factor。Load factor默认是0.75,是offers a good tradeoff between time and space costs。当map元素数量大于集合容量乘以加载因子时,集合容量扩大到当前容量的两倍,并且对元素进行rehash。尽量避免rehash,如果初始容量大于最大元素数除以加载因子时,则不会发生rehash。

 

2)非线程安全。

典型的问题是多线程并发访问、rehash时,会产生死循环问题。可使用Collections.synchronizedMap 方法来“包装”该映射,实现并发的安全处理。如果Collections.synchronizedMap不能满足性能要求,可试试选择ConcurrentHashMap。

 

3)迭代的快速失败。

集合的迭代器创建之后,如果集合结构有修改,而且非调用Iterator.remove(),迭代器会抛出 ConcurrentModificationException,但是该fail-fast得不到保证的。参考iterator方法,可知在遍历Iterator期间,元素个数有变化,则出现并发问题,抛出异常。

 

Step1
private final class KeySet extends AbstractSet<K> {
  public Iterator<K>iterator() {
    return new KeyIterator();
  }
  publicint size() {
    returnsize;
  }
  public boolean contains(Object o) {
    return containsKey(o);
  }
  public boolean remove(Object o) {
    return HashMap.this.removeEntryForKey(o) != null;
  }
  public void clear() {
    HashMap.this.clear();
  }
}
Step2
Iterator<K> newKeyIterator()   {
  retur nnew KeyIterator();
}
Step3
private final class KeyIterator extends HashIterator<K> {
  public K next() {
    return nextEntry().getKey();
  }
}
Step4
final Entry<K,V> nextEntry() {
  if (modCount != expectedModCount)
    throw new ConcurrentModificationException();

  Entry<K,V> e = next;
  if (e == null)
    throw new NoSuchElementException();

  if ((next = e.next) == null) {
      Entry[] t = table;
      while (index< t.length&& (next = t[index++]) == null)
        ;
  }
current = e;
return e;
}

 

4)线程安全

HashMap为什么是非线程安全的?ConcurrentHashMap为什么是线程安全的?

HashMap数据结构是Entry数组,并发操作时,数组是被竞争的共享资源,如果读时有写操作,就会出现并发问题。

 

HashMap提供了fast-fail机制,使用Iterator遍历时,会比较元素更新数,如果元素数有变化,则抛出ConcurrentModificationException。

 

ConcurrentHashMap数据结构是Segment数组,每个数组上通过加显示锁保证了线程安全。同时,由于元素分成了多个segment,提高了并发性能。实现参考ConcurrentHashMap部分。

 

3.LinkedHashMap

1)数据结构

数据保存在一个数组,数组元素是双向链表。

private transient Entry<K,V>header;//数组
 
private static class Entry<K,V> extends HashMap.Entry<K,V>{
Entry<K,V> before, after;//双向指针
…
}

  

2)特点

LinkedHashMap保存了数据的插入顺序。在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的;HashMap返回key-value的顺序只与Key的hashCode有关。

 

LinkedHashMap也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和容量有关,而HashMap的遍历速度和他的碰撞情况相关。

 

5.ConcurrentHashMap

1)数据结构



 

 

2)特点

a)并发安全

通过对segment对象加锁,实现了该segment内读写操作的互斥,保证了并发安全性。 

 

b)并发性能好

ConcurrentHashMap数据保存在名为segments的数组中,锁是分别加在每个segement上的,与Hashtable、Collections.synchronizedMap相比,多个segment之间可以安全的并发操作,提高了并发性能。

 

代码实现

参考获取操作的实现代码。

Step1
public V get(Object key) {
  int hash = hash(key.hashCode());
  return segmentFor(hash).get(key, hash);
}

Step2
V get(Object key, int hash) {
  if (count != 0) { // read-volatile
    HashEntry<K,V> e = getFirst(hash);

  while (e != null) {
    if (e.hash == hash && key.equals(e.key)) {
      V v = e.value;
      if (v != null)
        return v;
      return readValueUnderLock(e); // recheck
    }
    e = e.next;
  }
 }
 return null;
}

Step3
V readValueUnderLock(HashEntry<K,V> e) {
  lock();
  try {
    return e.value;
  } finally {
    unlock();
  }
}

 

6.TreeMap

TreeMap数据结构是红黑树。实现了SortMap接口,能够把它保存的记录根据键排序,默认是按键值升序排序,也可以指定排序的比较器。(数据结构及分析待补充)

 

  • 大小: 13.5 KB
  • 大小: 36.7 KB
0
1
分享到:
评论

相关推荐

    MAP文件浅析(正点原子)-V1.0

    MAP文件浅析(正点原子)_V1.0

    lidaohang#ceph_study#Ceph OSDMap 机制浅析1

    在 OSDMap 数据中 Pool 集合,副本数,PG 数量,OSD 集合这 4 项由运维人员来指定,虽然 OSD 的状态也可以由运维人员进行更改,但是实际运行

    基于MAPSOURCE交换格式实现GPS航点批量输入浅析.pdf

    基于MAPSOURCE交换格式实现GPS航点批量输入浅析.pdf

    浅析go中的map数据结构字典

    1. map的使用 golang中的map是一种数据类型,将键与值绑定到一起,底层是用哈希表实现的,可以快速的通过键找到对应的值。 类型表示:map[keyType][valueType] key一定要是可比较的类型(可以理解为支持==的操作),...

    浅析scala中map与flatMap的区别

    主要介绍了浅析scala中map与flatMap的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结

    1.map 有返回值,返回一个新的数组,每个元素为调用func的结果。 let list = [1, 2, 3, 4, 5]; let other = list.map((d, i) =&gt; { return d * 2; }); console.log(other); // print: [2, 4, 6, 8, 10] 2.filter 有...

    浅析php中array_map和array_walk的使用对比

    一、array_map()  1、array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组,若函数作用后无返回值,则对应的新值数组中为空。  2、回调函数接受的参数数目...

    浅析Java8 中 Map 接口的新方法

    主要介绍了Java8 中 Map 接口的新方法,本文通过代码实例给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

    浅析Java中Map与HashMap,Hashtable,HashSet的区别

    HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对);HashSet则实现了Set接口,性质类似于集合

    Python中的map、reduce和filter浅析

    1、先看看什么是 iterable 对象 以内置的max函数为例子,查看其doc:复制代码 代码如下:&gt;&gt;&gt; print max.__doc__max(iterable[, key=func]) -&gt; valuemax(a, b, c, …[, key=func]) -&gt; value With a single iterable ...

    浅析java中遍历map的两种方式

    本篇文章主要分享了java中遍历map的两种方式,代码简洁明了,有需要的朋友可以参考下,希望会有所帮助

    浅析stl序列容器(map和set)的仿函数排序

    问题:set是一个自动有序的集合容器,这是set的一个最实惠的性质,从小到大,只要你插入进去,就有序了。但是,如果你不想要这个顺序呢,是不是可以人为控制set容器的元素顺序呢?答案是,可以的,因为stl也是程序员...

    Java 集合浅析.txt

    概况介绍了java中使用到的集合类,Collection,Map 及他们的继承子类 以及之间的区别

    浅析vue中常见循环遍历指令的使用 v-for

    vue中循环遍历使用的指令是v-for 1.v-for遍历数组 (1)value in arr 遍历数组中的元素 (2)(value,index) in arr 遍历数组中的元素和数组下标 运行代码: &lt;body&gt; ... &lt;li v-for=value&gt;{{value}}&lt;/li&gt;&lt;br&gt;  ...

    SprngMV浅析

    Controller接收request, response参数,然后返回ModelAndView(其中的Model不是Object类型,而是Map类型)。但在其它的MVC框架中,Action返回值一般都只是一个View Name;Model则需要通过其它的途径(如request....

    java8源码-java8-analysis:浅析java8

    浅析java8 Stream Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections. 1.代码 List strList = new ArrayList&lt;&gt;(); strList.add("1");...

Global site tag (gtag.js) - Google Analytics