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接口,能够把它保存的记录根据键排序,默认是按键值升序排序,也可以指定排序的比较器。(数据结构及分析待补充)
相关推荐
MAP文件浅析(正点原子)-V1.0 MAP文件浅析是MDK编译生成文件之一,顾名思义,它是一种映射文件,记录了编译过程中的各个阶段信息,包括生成的目标文件、符号表、程序段交叉引用关系、映像内存分布图等重要信息。...
在 OSDMap 数据中 Pool 集合,副本数,PG 数量,OSD 集合这 4 项由运维人员来指定,虽然 OSD 的状态也可以由运维人员进行更改,但是实际运行
《基于MAPSOURCE交换格式实现GPS航点批量输入浅析》 本文主要探讨如何利用MAPSOURCE交换格式实现GPS航点的批量输入,以提高地质工作的效率。Mapsource是一款常见的GPS数据处理软件,它允许用户在GPS设备和计算机...
Go语言中的map是一种内置的数据结构,它实现为哈希表,提供键(Key)到值(Value)的存储能力。使用map可以实现快速的键值查找,其核心特点包括: 1. 键必须是可比较的类型,即支持使用"=="操作符进行比较。例如,...
浅析Java8 中 Map 接口的新方法 Java8 中 Map 接口的新方法是指 Java8 中引入的一些新的方法,用于提高 Map 接口的使用效率和便捷性。在本文中,我们将详细介绍 Java8 中 Map 接口的新方法,并通过代码实例来演示其...
Scala 中 map 与 flatMap 的区别 在 Scala 中,map 和 flatMap 是两个非常重要的函数,它们都是函数式编程中的核心概念。虽然它们都可以将一个函数应用于集合中的每个元素,但是它们之间存在着很大的区别。 首先,...
1.map 有返回值,返回一个新的数组,每个元素为调用func的结果。 let list = [1, 2, 3, 4, 5]; let other = list.map((d, i) => { return d * 2; }); console.log(other); // print: [2, 4, 6, 8, 10] 2.filter 有...
《WordCount 源码浅析(1)》 在大数据处理领域,Hadoop 是一个不可或缺的名字,而 WordCount 是 Hadoop 的经典示例程序,它用于统计文本中单词出现的频率。这篇博客将对 WordCount 的源码进行初步解析,帮助初学者...
在Java编程中,Map接口是一个非常重要的数据结构,它存储键值对,其中每个键都是唯一的。遍历Map是开发过程中常见的操作,通常有两种主要的方法:通过Entry Set和通过Key Set。下面将详细介绍这两种遍历方式。 1. ...
在本文中,我们将深入探讨如何使用仿函数对STL序列容器`map`和`set`进行自定义排序。 `set`和`map`都是STL中有序的容器。`set`是一个无重复元素的集合,而`map`则是一组键值对,其中键是唯一的。它们都使用红黑树...
- **map vs walk**:`array_map()` 是为了获取处理后的新数组,关注结果;而 `array_walk()` 更注重过程,可以用于执行不返回值的操作,如调试、打印或直接修改数组。 - **参数传递**:`array_walk()` 可以通过 `$...
在Java编程语言中,`Map`接口是用于存储键值对的数据结构,它不保证元素的顺序,允许null键和null值。`HashMap`、`Hashtable`和`HashSet`都是基于`Map`或`Set`接口实现的不同数据结构,它们在功能、线程安全性和性能...
《Hadoop应用系列2--MapReduce原理浅析(上)》 MapReduce是Apache Hadoop框架的核心组件之一,主要用于处理和生成大规模数据集。本文将深入浅出地探讨MapReduce的工作原理,帮助读者理解其核心概念和流程。 一、...
1、先看看什么是 iterable 对象 以内置的max函数为例子,查看其doc:复制代码 代码如下:>>> print max.__doc__max(iterable[, key=func]) -> valuemax(a, b, c, …[, key=func]) -> value With a single iterable ...
### 浅析MFC程序基本运行机制 #### 一、MFC简介与特点 MFC(Microsoft Foundation Classes)是微软为简化Windows编程而提供的一套类库,它基于C++语言,封装了大量的Windows API,使得开发者能够更高效、更简单地...
### Hadoop+HDFS和MapReduce架构浅析 #### 摘要 本文旨在深入剖析Hadoop中的两大核心组件——HDFS(Hadoop Distributed File System)和MapReduce的工作原理及其实现机制。首先,我们将介绍Hadoop NameNode与...
### Java集合浅析 #### 一、概述 Java集合框架是Java编程语言中处理数据结构的一个强大工具包,它提供了一系列灵活高效的接口和实现来帮助开发者管理数据。本篇文章将重点介绍Java中常用的集合类——`Collection`...
SliveTest是另一种专门用于测试Namenode性能的工具,它通过大量map任务模拟各种RPC操作,如ls(列出文件和目录)、append(追加写文件)、create(创建文件)、delete(删除文件)、mkdir(创建目录)、rename(重命名文件)和...