一、概述
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
Map接口和Collection接口不同之处:
- Map是双列的,Collection是单列的
- Map的键唯一,Collection的子体系Set是唯一的
- Map集合的数据结构值针对键有效(HashMap即键使用Hash算法),跟值无关;Collection集合的数据结构是针对元素有效
- Set底层依赖的是map,只是值(new Object())被隐藏。好处是两个体系只需要一套算法,其中一个只需要隐藏值即可。
例如HashSet中部分源码:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
二、常用方法
1.添加功能
V
put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
注:如果键是第一次存储,就直接存储元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值。
由于put方法比较重要,面试中经常考察,所以对于源码的解析我单独放在一篇文章中,详情请看:
public static void demo1() {
Map<String, Integer> map = new HashMap<>();
Integer i1 = map.put("张三", 23);
Integer i2= map.put("李四", 24);
Integer i3 = map.put("张三", 26); //相同的键不存储,值覆盖,把被覆盖的值返回
System.out.println(map);
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);
}
/*
{李四=24, 张三=26}
null
null
23
*/
2.删除功能
void
clear()
从此映射中移除所有映射关系(可选
V
remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
public void clear() {
Node<K,V>[] tab;
modCount++; //修改次数+1
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
clear()方法将数组内每一个指向都赋值为null,则各个Node节点对象就不会被引用,而现代的虚拟机都采用了可达性分析算法来判断一个对象是否存活,当一个对象不能通过任何引用找到它就认为它是不可达的,从而视为可回收的对象。
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
public static void demo2() {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
Integer value = map.remove("张三"); //根据键删除元素,返回键对应的值
System.out.println(value); //23
}
3.判断功能
boolean
containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。boolean
containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
boolean
isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
4.获取功能
V
get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null
。
Set<K>
keySet()
返回此映射中包含的键的Set
视图。
Collection<V>
values()
返回此映射中包含的值的Collection
视图。
Set<Map.Entry<K,V>>
entrySet()
返回此映射中包含的映射关系的Set
视图。
注:因为key没有重复值,所以可以使用Set集合存储。
5.长度功能
int
size()
返回此映射中的键-值映射关系数。
三、双列集合遍历方法
1.根据键寻找值
思路:
- 获取所有键的集合
- 遍历键的集合,获取到每一个键
- 根据键找值
实现:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
//获取所有的键
Set<String> keySet = map.keySet(); //获取所有键的集合
Iterator<String> it = keySet.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素
String key = it.next(); //获取每一个键
Integer value = map.get(key); //根据键获取值
System.out.println(key + "=" + value);
}
}
/*
李四=24
张三=23
王五=25
赵六=26
*/
由于通过迭代器遍历代码比较复杂,所以可以用到我们之前所学的增强for循环,底层也为迭代器实现,但具体的代码比较简单
for(String key : map.keySet()) { //map.keySet()是所有键的集合
System.out.println(key + "=" + map.get(key));
}
2.键值对对象找键和值
思路:
- 获取所有键值对对象的集合
- 遍历键值对对象的集合,获取到每一个键值对对象
- 根据键值对对象找键和值
实现:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
//Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象,并存储在Set集合中
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
//获取每一个对象
Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
while(it.hasNext()) {
//获取每一个Entry对象
Map.Entry<String, Integer> en = it.next(); //父类引用指向子类对象
//Entry<String, Integer> en = it.next(); //直接获取的是子类对象
String key = en.getKey(); //根据键值对对象获取键
Integer value = en.getValue(); //根据键值对对象获取值
System.out.println(key + "=" + value);
}
}
同样我们可以使用更为间接的增强for循环实现:
for(Entry<String, Integer> en : map.entrySet()) {
System.out.println(en.getKey() + "=" + en.getValue());
}
注:Map.Entry解释Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象,正如下面的方式
interface Inter {
interface Inter2 {
public void show();
}
}
class Demo implements Inter.Inter2 {
@Override
public void show() {
}
}
四、其它Map子类特性
1.LinkedHashMap
底层是链表实现,可以保证怎么存就怎么取
2.TreeMap
底层是红黑树实现,可以根据给定比较方式排序