Map 详解-用法,遍历,排序,常用API等
1. Map的概要
java.util 中的集合类包含java中某些最常用的类。最常用的集合类型是List和Map。
Map提供了一个更通用的元素存储方法。Map集合类用于存储元素对(称作“键”和“值”),其中每一个键映射到一个值。
2. Map的用法
2.1 类型介绍
java自带了各种Map类。这些Map类可归为三种类型:
1)通用Map,用于在应用程序中管理映射,通常在java.util程序包中实现HashMap、Hashtable、Properties、LinkedHashMap、IdentityHashMap、TreeMap、WeakHashMap、ConcurrentHashMap
2)专用Map,通常我们不必亲自创建此类Map,而是通过某些其他类进行访问java.util.jar.Attributes、javax.print.attribute.standard.PrinterStateReasons、java.security.Provider、java.awt.RenderingHints、javax.swing.UIDefaults
3)一个用于帮助我们实现自己的Map类的抽象类
AbstractMap
2.2 类型区别
HashMap
最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。
TreeMap
能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
Hashtable
与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
LinkedHashMap
保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。
3.Map基本用法
3.1 Map初始化
Map<String, String> map = new HashMap<String, String>();
3.2 插入元素
map.put("key1", "value1");
3.3 获取元素
map.get("key1")
3.4 移除元素
map.remove("key1");
3.5 清空Map
map.clear();
3.6 四种常用的Map插入和读取性能比较
测试环境:jdk 1.8
测试代码
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.UUID;
public class Test {
static int hashMapW = 0;
static int hashMapR = 0;
static int linkMapW = 0;
static int linkMapR = 0;
static int treeMapW = 0;
static int treeMapR = 0;
static int hashTableW = 0;
static int hashTableR = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Test test = new Test();
test.test(100 * 10000);
System.out.println();
}
System.out.println("hashMapW = " + hashMapW / 10);
System.out.println("hashMapR = " + hashMapR / 10);
System.out.println("linkMapW = " + linkMapW / 10);
System.out.println("linkMapR = " + linkMapR / 10);
System.out.println("treeMapW = " + treeMapW / 10);
System.out.println("treeMapR = " + treeMapR / 10);
System.out.println("hashTableW = " + hashTableW / 10);
System.out.println("hashTableR = " + hashTableR / 10);
}
public void test(int size) {
int index;
Random random = new Random();
String[] key = new String[size];
// HashMap 插入
Map<String, String> map = new HashMap<String, String>();
long start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
key[i] = UUID.randomUUID().toString();
map.put(key[i], UUID.randomUUID().toString());
}
long end = System.currentTimeMillis();
hashMapW += (end - start);
System.out.println("HashMap插入耗时 = " + (end - start) + " ms");
// HashMap 读取
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
index = random.nextInt(size);
map.get(key[index]);
}
end = System.currentTimeMillis();
hashMapR += (end - start);
System.out.println("HashMap读取耗时 = " + (end - start) + " ms");
// LinkedHashMap 插入
map = new LinkedHashMap<String, String>();
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
key[i] = UUID.randomUUID().toString();
map.put(key[i], UUID.randomUUID().toString());
}
end = System.currentTimeMillis();
linkMapW += (end - start);
System.out.println("LinkedHashMap插入耗时 = " + (end - start) + " ms");
// LinkedHashMap 读取
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
index = random.nextInt(size);
map.get(key[index]);
}
end = System.currentTimeMillis();
linkMapR += (end - start);
System.out.println("LinkedHashMap读取耗时 = " + (end - start) + " ms");
// TreeMap 插入
key = new String[size];
map = new TreeMap<String, String>();
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
key[i] = UUID.randomUUID().toString();
map.put(key[i], UUID.randomUUID().toString());
}
end = System.currentTimeMillis();
treeMapW += (end - start);
System.out.println("TreeMap插入耗时 = " + (end - start) + " ms");
// TreeMap 读取
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
index = random.nextInt(size);
map.get(key[index]);
}
end = System.currentTimeMillis();
treeMapR += (end - start);
System.out.println("TreeMap读取耗时 = " + (end - start) + " ms");
// Hashtable 插入
key = new String[size];
map = new Hashtable<String, String>();
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
key[i] = UUID.randomUUID().toString();
map.put(key[i], UUID.randomUUID().toString());
}
end = System.currentTimeMillis();
hashTableW += (end - start);
System.out.println("Hashtable插入耗时 = " + (end - start) + " ms");
// Hashtable 读取
start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
index = random.nextInt(size);
map.get(key[index]);
}
end = System.currentTimeMillis();
hashTableR += (end - start);
System.out.println("Hashtable读取耗时 = " + (end - start) + " ms");
}
}
测试结果
w/ms | 插入10次平均(ms) | 读取10次平均(ms) | ||||
---|---|---|---|---|---|---|
测试赋值 | 1w | 10w | 100w | 1w | 10w | 100w |
HashMap | 94 | 230 | 2246 | 2 | 15 | 214 |
LinkedHashMap | 40 | 193 | 2208 | 3 | 15 | 203 |
TreeMap | 52 | 266 | 3170 | 8 | 90 | 1057 |
Hashtable | 41 | 191 | 2077 | 2 | 17 | 232 |
4. Map 遍历(4种方式)
4.1 初始化数据
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
4.2 增强for循环 keySet()
使用keySet()遍历
for (String key : map.keySet()) {
System.out.println(key + " :" + map.get(key));
}
4.3 增强for循环 entrySet()
使用entrySet()遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " :" + entry.getValue());
}
4.4 迭代器遍历 keySet()
使用keySet()遍历
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + " :" + map.get(key));
}
4.5 迭代器遍历 entrySet()
使用entrySet()遍历
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " :" + entry.getValue());
}
5 Map 排序
HashMap、Hashtable、LinkedHashMap排序
Map<String, String> map = new HashMap<String, String>();
map.put("a", "c");
map.put("b", "b");
map.put("c", "a");
// 通过ArrayList构造函数把map.entrySet()转换成list
List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(map.entrySet());
// 通过比较器实现比较排序
Collections.sort(list, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> mapping1, Map.Entry<String, String> mapping2) {
return mapping1.getKey().compareTo(mapping2.getKey());
}
});
for (Map.Entry<String, String> mapping : list) {
System.out.println(mapping.getKey() + " :" + mapping.getValue());
}
TreeMap排序
TreeMap默认按key进行升序排序,如果想改变默认的顺序,可以使用比较器:
Map<String, String> map = new TreeMap<String, String>(new Comparator<String>() {
public int compare(String obj1, String obj2) {
return obj2.compareTo(obj1);// 降序排序
}
});
map.put("a", "c");
map.put("b", "b");
map.put("c", "a");
for (String key : map.keySet()) {
System.out.println(key + " :" + map.get(key));
}
按value排序(通用)
Map<String, String> map = new TreeMap<String, String>();
map.put("a", "c");
map.put("b", "b");
map.put("c", "a");
// 通过ArrayList构造函数把map.entrySet()转换成list
List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(map.entrySet());
// 通过比较器实现比较排序
Collections.sort(list, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> mapping1, Map.Entry<String, String> mapping2) {
return mapping1.getValue().compareTo(mapping2.getValue());
}
});
for (String key : map.keySet()) {
System.out.println(key + " :" + map.get(key));
}
5.1 HashMap四种遍历方式性能测试
比较方式:
分别对四种遍历方式进行50W次迭代,比较用时。
代码:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Test;
public class TestMap {
private final static int SIZE = 500000;
public static void main(String[] args) {
// 初始化 50w赋值
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < SIZE; i++) {
map.put(i, i);
}
/**
* 增强for循环,keySet迭代
* 1)21ms
* 2)15ms
* 3)15ms
* 平均值:17ms
*/long start = System.currentTimeMillis();
for (Integer key : map.keySet()) {
map.get(key);
}
long end = System.currentTimeMillis();
System.out.println("增强for循环,keySet迭代 -> " + (end - start) + " ms");
/**
* 增强for循环,entrySet迭代
* 1)14ms
* 2)17ms
* 3)14ms
* 平均值:15ms
*/
start = System.currentTimeMillis();
for (Entry<Integer, Integer> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
end = System.currentTimeMillis();
System.out.println("增强for循环,entrySet迭代 -> " + (end - start) + " ms");
/**
* 迭代器,keySet迭
* 1)14ms
* 2)11ms
* 3)13ms
* 平均值:12.6ms
*/
start = System.currentTimeMillis();
Iterator<Integer> iterator = map.keySet().iterator();
Integer key;
while (iterator.hasNext()) {
key = iterator.next();
map.get(key);
}
end = System.currentTimeMillis();
System.out.println("迭代器,keySet迭代 -> " + (end - start) + " ms");
/**
* 迭代器,entrySet迭代
* 1)13ms
* 2)11ms
* 3)9ms
* 平均值:11ms
*/
start = System.currentTimeMillis();
Iterator<Map.Entry<Integer, Integer>> iterator1 = map.entrySet().iterator();
Map.Entry<Integer, Integer> entry;
while (iterator1.hasNext()) {
entry = iterator1.next();
entry.getKey();
entry.getValue();
}
end = System.currentTimeMillis();
System.out.println("迭代器,entrySet迭代 -> " + (end - start) + " ms");
}
}
增强for循环,keySet迭代三次压力测试结果
/**
* 增强for循环,keySet迭代
* 1)21ms
* 2)15ms
* 3)15ms
* 平均值:17ms
*/
增强for循环,entrySet迭代三次压力测试结果
/**
* 增强for循环,entrySet迭代
* 1)14ms
* 2)17ms
* 3)14ms
* 平均值:15ms
*/
迭代器,keySet迭代三次测试结果
/**
* 迭代器,keySet迭
* 1)14ms
* 2)11ms
* 3)13ms
* 平均值:12.6ms
*/
迭代器,entrySet迭代三次测试结果**
/**
* 迭代器,entrySet迭代
* 1)13ms
* 2)11ms
* 3)9ms
* 平均值:11ms
*/
总结:
1)增强for循环使用方便,但性能较差,不适合处理超大量级的数据
2)迭代器的遍历速度要比增强for循环快很多,是增强for循环的1倍左右
3)使用entrySet遍历的速度要比keySet要快
6 常用API
方法 | 注解 |
---|---|
clear() | 从Map中删除所有映射 |
remove(Object key) | 从 Map 中删除键和关联的值 |
put(Object key, Object value) | 将指定值与指定键相关联 |
putAll(Map t) | 将指定 Map 中的所有映射复制到此 map |
entrySet() | 返回 Map 中所包含映射的 Set 视图。Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素 |
keySet() | 返回 Map 中所包含键的 Set 视图。删除 Set 中的元素还将删除 Map 中相应的映射(键和值) |
values() | 返回 map 中所包含值的 Collection 视图。删除 Collection 中的元素还将删除 Map 中相应的映射(键和值) |
get(Object key) | 返回与指定键关联的值 |
containsKey(Object key) | 如果 Map 包含指定键的映射,则返回 true |
containsValue(Object value) | 如果此 Map 将一个或多个键映射到指定值,则返回 true |
isEmpty() | 如果 Map 不包含键-值映射,则返回 true |
size() | 返回 Map 中的键-值映射的数目 |